Skip to content

Instantly share code, notes, and snippets.

@milkbread
Created June 27, 2013 07:50
Show Gist options
  • Save milkbread/5874692 to your computer and use it in GitHub Desktop.
Save milkbread/5874692 to your computer and use it in GitHub Desktop.
HTML: ARC-Aggregation #1 - example with minimum clipped dataset
<!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="http://bl.ocks.org/milkbread/raw/5779939/RKMapping_0.4.4.js"></script> <!--http://bl.ocks.org/milkbread/raw/5779939-->
<script src="http://bl.ocks.org/milkbread/raw/5829814/RKAggregation_1.0.js"></script> <!--http://bl.ocks.org/milkbread/raw/5829814 this function needs RKMapping.js additionally-->
<script src="/milkbread/raw/5743667/RKToolbox.js"></script>
<style>
@import url(http://bl.ocks.org/milkbread/raw/5743667/RKToolbox_0.1.css);
@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: 400px;
}
#polys:hover{
fill:rgba(200,0,0,0.8);
}
#polys{
stroke-width:1px;
stroke:rgba(255,255,255,1);
}
.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);
}
.axis text {
font-family: sans-serif;
font-size: 12px;
text-anchor:left;
fill:#0f0;
opacity:0.8;
}
div {
margin-bottom: 0px;
}
</style>
</head>
<body>
<div id=slider></div>
<div id=map></div>
<script>
//The slider
var slider = new slider(d3.select("#slider"),960,75);
slider.reDefineIndicator(6)
//The background map
var map = L.map('map').setView([49.36, 7.36], 9); //#1
//var map = L.map('map').setView([50.23, 7.68], 8); //#2
//var map = L.map('map').setView([50.13, 7.66], 7); //#3
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("/milkbread/raw/5754788/vg250_clipped_mini_topo.json", function(error, topology) { //#1
//d3.json("/milkbread/raw/5754788/vg250_clipped3_topo.json", function(error, topology) { //#2
//d3.json("/milkbread/raw/5754788/vg250_clipped2_topo.json", function(error, topology) { //#3
//get the pure 'data-object', containing only id, type & arcs
var pure_states = topology.objects.vg250_gem_clipped3;
//initialise a storage for the removed Features
var removedFeatureStorage = new remFeatureStorage();
//create a feature storage
var featureStorage = new featureObjStorage();
//store the highest value of 'SHAPE_AREA'
var maxVal = 0;
//add each pure 'data-object' to the feature storage
pure_states.geometries.forEach(function(d){
featureStorage.addFeatObj(d)
if(d.properties.SHAPE_AREA>maxVal)maxVal=d.properties.SHAPE_AREA;
})
//define the dimension of the slider
slider.addScalebar([0,maxVal]);
slider.addHoverListener(hoverAction);
//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);
//########AGGREGATION - MODULS########################
function hoverAction(){
var indicatorPositionHover=slider.getIndicPosition()
//++++++++FEATURE FILTERING and SORTING+++++++++++++++
//check whose size is too small...it it is, push its id into an array
var filteredFeatures = [];
featuresObjects.forEach(function(d){
if(d.properties.SHAPE_AREA < indicatorPositionHover){
if(d.type=='Polygon')filteredFeatures.push([d.id,-1])
else if(d.type=='MultiPolygon'){
//console.log(d.geometry.length)
for(var i=d.geometry.length-1;i>=0;i--){
filteredFeatures.push([d.id,i])
}
//filteredFeatures.push([d.id,1])
//filteredFeatures.push([d.id,0])
}
}
else return;
})
//++++++++FINISHED FILTERING --> 'filteredFeatures'++
//~~~~~~~~INITIATE MAIN AGGREGATION MODULS~~~~~~~~~~~~
filteredFeatures.forEach(function(d){ //aggregate each feature, whose id is stored in this array...feature objects are directly edited by this function
//console.log("####Begin feature aggregation#####")
doAggregation(d[0], d[1])
//console.log("###Finished feature aggregation###")
})
//re-show the aggreagated features
overLayer.removeAll();
polygons = getGeometries(featuresObjects, arcCollection)
overLayer.addGeometries(polygons,"path");
overLayer.showAll();
doStyling();
}
function doAggregation(id, indexOfMultiPolyPart){ //having this all as seperate function makes it applicable flixible...so I can use it also on click or something else
//console.log("indexOfMultiPolyPart: ",indexOfMultiPolyPart)
var featureObjectsIDs = featureStorage.getIndizes(); //stores the IDs of all features ... improves search & -speed
var selectedFeatureIndex = featureObjectsIDs.indexOf(id); //get the index of the current feature in the storage
var analysedFeature = featuresObjects[selectedFeatureIndex]; //get the current feature from the storage, position in array = 'selectedFeatureIndex'
//console.log("analysedFeature: ",analysedFeature, "multipolypart: ",indexOfMultiPolyPart)
//++++++++GET NEIGHBORS+++++++++++++++
var neighbors = analysedFeature.getNeighbors(arcCollection, indexOfMultiPolyPart); //get all neighbors of the current feature ... 'indexOfMultiPolyPart' indicates the position of the chosen MultiPolygon-part
//console.log(neighbors)
//++++++++FINISHED GET NEIGHBORS --> 'neighbors'++
//++++++++ANALYSE NEIGHBORS+++++++++++++++
if(analysedFeature.type=='Polygon') var analysed_geometry = analysedFeature.geometry[0];
else if(analysedFeature.type=='MultiPolygon') var analysed_geometry = analysedFeature.geometry[indexOfMultiPolyPart][0];
var neighType = analyseNeighbors(neighbors, analysedFeature.type, analysed_geometry); //possible results: isle, hole, mesh_element, undefined
//console.log(neighType);
//++++++++FINISHED ANALYSE NEIGHBORS --> 'neighbor type'++
if(neighType=='hole' || neighType=='mesh_element'){ //only go on when there are some neighbors
//++++++++FIND BEST FITTING PARTNER FOR AGGREGATION (NEIGHBOR)+++++++++++++++... Different solutions are possible
var sorted_neighbors = sortArcs(neighbors, arcCollection)
var aggregationPartner = {};
aggregationPartner.id = neighbors[0].id;
aggregationPartner.direction = neighbors[0].direction;
aggregationPartner.arc_id = neighbors[0].arc_id;
aggregationPartner.index = neighbors[0].index;
//++++++++FINISHED SEARCH --> 'aggregationPartner'++
//++++++++CONTROLL EXISTENCE & CORRECT ID OF AGGREGATION PARTNER+++++++++++++++
var control = false, next = 1;
while (control == false){
aggregationPartner = controlExistenceAndCorrect(id, aggregationPartner, arcCollection, removedFeatureStorage, sorted_neighbors, featuresObjects, featureObjectsIDs, indexOfMultiPolyPart, neighType)
if(aggregationPartner!=undefined) control=true;
else if(sorted_neighbors[next]!=undefined){aggregationPartner = getNeighborCopy(sorted_neighbors[next]); next++;}
else control=true;
}
//console.log("aggregation partner: ",aggregationPartner)
//++++++++FINISHED CONTROLL & CORRECTION --> 'aggregationPartner' with correct id+++++++++++++++
if(aggregationPartner==undefined) console.log("The existence control did gave back some bogus...but why?")
else{
//++++++++AGGREGATION - CORE+++++++++++++++
var aggregationPartnerIndex = featureObjectsIDs.indexOf(aggregationPartner.id); //now we have to find the position of the corresponding feature in the array
var aggregationPartnerFeature = featuresObjects[aggregationPartnerIndex]; //by the found index...we get the corresponding feature
aggregateFeatures(analysedFeature, aggregationPartner, aggregationPartnerFeature, arcCollection, neighType);
//++++++++FINISHED AGGREGATION - CORE+++++++++++++++
//++++++++REMOVE FROM 'allFeatures' & ADD to 'removedFeature' (analysed feature)+++++++++++++++
featureStorage.removeFeatObj(analysedFeature, removedFeatureStorage, aggregationPartnerFeature, indexOfMultiPolyPart)
//++++++++FINISHED REMOVE & ADD+++++++++++++++
}
}else console.log(id + " - There is no neighbor!!!")
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//~~~~~~~~STANDARD VISUALISATION OF FEATURES~~~~~~~~~~
//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();
//additional 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();
}
//###Basic functions ... that are not related to the polygonal aggregation!
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/1000000).toFixed(4)))
//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