Skip to content

Instantly share code, notes, and snippets.

@milkbread
Created June 21, 2013 08:49
Show Gist options
  • Save milkbread/5829835 to your computer and use it in GitHub Desktop.
Save milkbread/5829835 to your computer and use it in GitHub Desktop.
HTML: 2nd example on arc-aggregation

#D3 + TopoJSON -- Aggregation ##this is a second demonstration of my algorithm for aggregating adjacent polygons based on their redundant arc geometries

  • origin is the file RKMapping_clipping_1.1.html
<!DOCTYPE html>
<html>
<head>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script src="http://d3js.org/topojson.v1.min.js"></script>
<script src="http://cdn.leafletjs.com/leaflet-0.5.1/leaflet.js"></script>
<!--<script src="https://gist.github.com/milkbread/5713629/raw/RKMapping_0.4.js"></script>-->
<script src="../static/RKMapping_0.4.4.js"></script> <!--http://bl.ocks.org/milkbread/raw/5779939-->
<script src="../static/RKAggregation.js"></script> <!--http://bl.ocks.org/milkbread/raw/5829814 this function needs RKMapping.js additionally-->
<style>
@import url(http://bl.ocks.org/milkbread/raw/5713683/myStyles.css);
@import url(http://cdn.leafletjs.com/leaflet-0.5/leaflet.css);
#map {
width: 960px;
height: 600px;
}
#polys:hover{
fill:rgba(200,0,0,0.8);
}
#polys{
stroke-width:1px;
stroke:rgba(255,255,255,1);
}
#polys2{
stroke-width:3px;
stroke:rgba(255,0,0,1);
fill:rgba(0,255,0,0.5)
}
.overlay_poly{
fill:rgba(100,100,100,0.3);
}
.overlay_multipoly{
fill:rgba(0,0,0,0.7);
}
.clipped_poly{
fill:rgba(255,0,0,0.7);
}
#delaunay{
fill:none;
stroke:rgba(0,0,255,1);
stroke-width:1px;
}
#points{
fill:#000;;
}
</style>
</head>
<body>
<div id=map></div>
<script>
//The background map
//var map = L.map('map').setView([49.9, 7.76], 8);
var map = L.map('map').setView([50.19, 7.58], 11);
//var map = L.map('map').setView([49.53, 8.17], 11);
var stamen = L.tileLayer('http://{s}.tile.stamen.com/toner/{z}/{x}/{y}.png', {attribution: 'Add some attributes here!'}).addTo(map);
var baseLayers = {"stamen": stamen};
L.control.layers(baseLayers).addTo(map);
//initial D3 stuff
var overLayer = new mapOverlay(map);
var infoContainer = d3.select("body").append("info").text("This will show you some information!")
d3.select("body").append("br")
var mouseInfo = d3.select("body").append("info").text("")
var thousendsSep = d3.format(","), maximum = 0;
//load data
d3.json("../gists/milkbread/raw/5754788/vg250_clipped3_topo.json", function(error, topology) {
//d3.json("../gists/milkbread/raw/5754788/vg250_clipped_mini_topo.json", function(error, topology) {
//get the pure 'data-object', containing only id, type & arcs
var pure_states = topology.objects.vg250_gem_clipped4;
var maxID = 0;
pure_states.geometries.forEach(function(d){if (d.id>maxID)maxID=d.id})
console.log(maxID)
//initialise a storage for the removed Features
var removedFeatureStorage = new remFeatureStorage();
//create a feature storage
var featureStorage = new featureObjStorage();
//add each pure 'data-object' to the feature storage
var new_id = 2;
pure_states.geometries.forEach(function(d){
if(d.type=='MultiPolygon'){
d.arcs.forEach(function(polygon){
var newFeature = new Object();
newFeature.type='Polygon';
newFeature.id=maxID+new_id;
new_id++;
newFeature.properties=d.properties;
newFeature.arcs=polygon
featureStorage.addFeatObj(newFeature)
})
}
else featureStorage.addFeatObj(d) })
//get all feature objects from the storage ... feature objects contain all necessary objects and functions for the aggregation
var featuresObjects = featureStorage.featureObjects;
//sort the features in dependence of their shape size
featuresObjects.sort(function(a,b){return (b.properties.SHAPE_AREA-a.properties.SHAPE_AREA)})
//get all arc-geometries of the selected objects 'pure_states'...can be simplified directly
var arcCollection = getArcs(topology, featuresObjects);
//check whose size is too small...it it is, push its id into an array
var aggregFeatures = [];
featuresObjects.forEach(function(d){
if(d.properties.SHAPE_AREA< 6500){
if(d.type=='Polygon')aggregFeatures.push([d.id,-1])
else if(d.type=='MultiPolygon'){
//console.log(d.geometry.length)
for(var i=d.geometry.length-1;i>=0;i--){
//aggregFeatures.push([d.id,i])
}
//aggregFeatures.push([d.id,1])
//aggregFeatures.push([d.id,0])
}
}
else return;
})
//aggregate each feature, whose id is stored in this array...feature objects are directly edited by this function
aggregFeatures.forEach(function(d){
//console.log("###Begin the aggregation###")
doAggregation(d[0], d[1])
//console.log("###Finished the aggregation###")
})
//build geometries from feature and their arcs
polygons = getGeometries(featuresObjects, arcCollection)
//add the geometries to the data overlay
overLayer.addGeometries(polygons,"path");
//build a GeometryCollection, for the visualisation of features
var polyCollection = {type: 'GeometryCollection', geometries:polygons};
map.on("viewreset", reset);
doStyling();
reset();
//***end of basic operations
//###FUNCTIONS
function doAggregation(id, fixed_multiPolyIndex_){
var testSuccess = false;
var featuresObjectsIndizes = featureStorage.getIndizes(); //stores the IDs of all features ... improves search & -speed
var selectedFeatureIndex = featuresObjectsIndizes.indexOf(id); //get the index of the current feature in the storage
var featureObject = featuresObjects[selectedFeatureIndex]; //get the current feature from the storage, position in array = 'selectedFeatureIndex'
//console.log("This is the id of the analysed feature: ",featureObject.id, "... it is a: ",featureObject.type)
var neighbors = featureObject.getNeighbors(arcCollection, fixed_multiPolyIndex_); //get all neighbors of the current feature ... 'fixed_multiPolyIndex_' indicates the position of the chosen MultiPolygon-part
//console.log("These are my neighbors: ",neighbors)
if(neighbors.length>0){ //only go on when there are some neighbors
//~~~~Find/Define the partner of aggregation ... Different solutions are possible
var sorted_neighbors = sortArcs(neighbors, arcCollection)
console.log("id of analysed feature: ",id, "... and the sorted neighbors: ", sorted_neighbors)
var longest_neigh = checkNeighborExistence2(id, arcCollection, removedFeatureStorage, sorted_neighbors, featuresObjects, featuresObjectsIndizes)
//console.log(longest_neigh.id)
//~~~~~~~~~~~~~~~~
if(longest_neigh!=undefined){ //is undefined...when all possible neighbors where checked and are all removed
//console.log("...that means we want to aggregate this feature: ",longest_neigh.id," (add to candidate)", " with this feature: ",featureObject.id, "(origin) ")
//now we have to find the corresponding feature
var addToCandidateIndex = featuresObjectsIndizes.indexOf(longest_neigh.id);
//-1 means ... nothing found ... should not happen, as this is already catched by checkNeighborExistence()
//if(addToCandidateIndex==-1){ addToCandidateIndex = checkNeighborExistence_alternative(removedFeatureStorage, featuresObjectsIndizes, longest_neigh.id); }
var addToCandidate = featuresObjects[addToCandidateIndex];
//console.log("...and with help of the index: "+addToCandidateIndex+" ...I can find the add to candidate: ",addToCandidate)
//console.log("test: ",removedFeatureStorage)
//if there is more than one neighboring arc for the addToCandidate ... but not completely necessary
var additionalNeighborArcs = checkForRedundantFeatures(sorted_neighbors, longest_neigh)
//aggregate the selected feature to the 'best fitting' neighbor
var removedArc = addToCandidate.aggregateFeature(featureObject, longest_neigh, additionalNeighborArcs, arcCollection);
//console.log("This is the removed arc: ",removedArc)
var originIndex = featuresObjectsIndizes.indexOf(featureObject.id)
//console.log("Did I find the origin to remove it: ",originIndex)
//remove the -origin- feature from the storage
//console.log("Das array vorm Entfernen: ",featuresObjects.length)
featureStorage.removeFeatObj(featureObject, removedFeatureStorage, addToCandidate, fixed_multiPolyIndex_)
//console.log(" ... und danach: ",featuresObjects.length)
testSuccess = true;
}else console.log("!!!Cannot find any neighbor that was not already removed...fix this bug...please!!!",id)
}else console.log(id + " - There is no neighbor!!!")
return testSuccess;
}
//Basic functions ... that are not related to the polygonal aggregation!
//function for aggregation on click
function visualiseFeatures(data){
//This is a test on detecting which part of a MultiPolygon has to be edited ... !!!HAS TO BE REPLACED!!! against a check who fits to the used criterium ... currently the criterium is the click!
//console.log(data, d3.geo.bounds(data),d3.mouse(this));
var mouse_coords = map.layerPointToLatLng(d3.mouse(this))
//console.log(mouse_coords, mouse_coords.lat)
var fixed_multiPolyIndex = -1; //this variable is only defined for MultiPolygons and tells me which one of the 'multi'-possibilities the selected on is...by its index
if(data.type=='MultiPolygon')data.coordinates.forEach(function(d,i){
var this_bounds = d3.geo.bounds({type: 'Polygon', coordinates: d})
////console.log(this_bounds)
if((mouse_coords.lng>this_bounds[0][0] && mouse_coords.lng<this_bounds[1][0]) && (mouse_coords.lat>this_bounds[0][1] && mouse_coords.lat<this_bounds[1][1]))fixed_multiPolyIndex = i;
})
//++++++++++++++
overLayer.removeAll();
doAggregation(data.id, fixed_multiPolyIndex);
polygons = getGeometries(featuresObjects, arcCollection)
overLayer.addGeometries(polygons,"path");
overLayer.showAll();
doStyling();
}
function doStyling(){
overLayer.features.attr("class",function(d){if(d.clipped==true) return "clipped_poly"; else {if(d.type=="Polygon")return "overlay_poly"; else if(d.type=="MultiPolygon") return "overlay_multipoly"; }})
.attr("id","polys")
.on("mouseover",showInfo)
.on("mouseup",visualiseFeatures);
}
function reset(){
var start_time = Date.now();
overLayer.resetView(d3.geo.bounds(polyCollection));
overLayer.showAll();
console.log("initial load took: "+((Date.now() - start_time)/1000).toFixed(2)+"s")
}
function showInfo(value){
mouseInfo.text("Center: "+map.getCenter()+" Zoom: "+map.getZoom())
infoContainer.text("You hovered over: "+value.properties.GEN +' - Type: '+value.type +' - ID: '+value.id +' - Area: ' + thousendsSep(value.properties.SHAPE_AREA))
//console.log(' - neighbors: ', polygons_indexed[value.id].neighbors)
}
function zoomToObject(d){
var local_bounds = d3.geo.bounds(d),
center = new L.LatLng(local_bounds[0][1]+((local_bounds[1][1]-local_bounds[0][1])/2),local_bounds[0][0]+((local_bounds[1][0]-local_bounds[0][0])/2)),
southWest = new L.LatLng(local_bounds[0][1], local_bounds[0][0]),
northEast = new L.LatLng(local_bounds[1][1], local_bounds[1][0]);
var local_bounds = new L.LatLngBounds(southWest, northEast);
map.setView( center, map.getBoundsZoom( local_bounds));
}
})
function reShow(maxVal){
maximum = maxVal;
overLayer.showAllFiltered(maxVal);
}
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment