Skip to content

Instantly share code, notes, and snippets.

Last active December 18, 2015 13:49
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save milkbread/5792426 to your computer and use it in GitHub Desktop.
Save milkbread/5792426 to your computer and use it in GitHub Desktop.
HTML: D3-tutorial 1 - zoombar
<script src=""></script>
var zoomLevel = 10;
var size = 20;
var container ="body").append("div").attr("id","#container");
var width = 300, height = 200;
var svgContainer = container.append("svg").attr("width", width).attr("height", height);
.attr("x", 0)
.attr("y", 0)
.attr("width", 200 + size*2)
.attr("height", size * 3)
var zoomIn = svgContainer.append("rect")
.attr("x", size)
.attr("y", size)
.attr("width", size)
.attr("height", size)
var zoomOut = svgContainer.append("rect")
.attr("x", 200)
.attr("y", size)
.attr("width", size)
.attr("height", size)
var zoomText = svgContainer.append("text")
.text("Zoomlevel: "+zoomLevel)
.attr("x", size + 30)
.attr("y", size * 2)
function zoomOutAction(){
function zoomInAction(){
function anotherFunction(value){
console.log("You've clicked "+value);
zoomText.text("Zoomlevel: "+zoomLevel)
function RKAggregation_version(){
return "0.1.0"
//Aggregation - 'Objects'
//object that defines and administrates a polygonal feature
//Object-definition -- Feature-Object ... contains all necessary functions and variables of one feature:
//Type, ID, PROPERTIES, GEOMETRY, getArcsPure(), getGeometry(), getNeighbors()
function featureObj(feature_){
//this.feature = feature_;
this.type = feature_.type; =; =;
this.geometry = feature_.arcs;
this.original_geometry = feature_.arcs.slice();
this.clipped = false;
this.removed_multi_parts = [];
this.removed_multi_parts_aggregated_to = [];
this.aggregatedFeatures = [];
var has_hole = [];
//check if geometry has holes
if (this.geometry.length==2) has_hole[0] = true;
else has_hole[0] = false;
else if(this.type=='MultiPolygon'){
if(polygonGeom.length==2) has_hole.push(true);
else has_hole.push(false);
this.has_hole = has_hole;
//get all neighbors of this feature ... fundamentally based on 'getNeighborsOfFeature()'
function getNeighbors(arcCollection__, index){
//console.log("These are some aggregated features, that have to be observed for the neighbor detection: ",this.aggregatedFeatures)
if(this.type=='Polygon') return getNeighborsOfFeature(this.getArcsPure()[0],, arcCollection__, this.aggregatedFeatures)
else if (this.type=='MultiPolygon') return getNeighborsOfFeature(this.getArcsPure_multipoly(index)[0],, arcCollection__, this.aggregatedFeatures)
//get all arcs (pure) in one array...'arcs'
function getArcsPure_multipoly(index_){
//console.log("Compare geometries: ", this.geometry, " vs. ",this.geometry[index_])
var pure_arcs = readPoly(this.geometry[index_],'arcs');
return pure_arcs;
//get all arcs (pure) in one array...'arcs'
function getArcsPure(){
var pure_arcs = [], pure_arcs_index = [];
var cache = readPoly(this.geometry,'arcs');
pure_arcs = cache[0];
pure_arcs_index = cache[1];
else if (this.type=='MultiPolygon') {
this.geometry.forEach(function(polygon0, k){
var cache = readPoly(polygon0,'arcs', k);
return [pure_arcs, pure_arcs_index];
//get polygons singulary...'single'
function getSingleGeometries(){
var single_geoms = [];
if(this.type=='Polygon')single_geoms = readPoly(this.geometry,'single');
else if (this.type=='MultiPolygon') {
this.geometry.forEach(function(polygon0){single_geoms.push(readPoly(polygon0,'single')); })
return single_geoms;
function readPoly(polygon_parts, origin, multi_index){
var arcs = [], arcs_index = [];
if(multi_index == undefined)arcs_index.push([i,j])
else arcs_index.push([multi_index,i,j])
return [arcs, arcs_index];
else if(origin=='single'){
//var single = [];
//TODO: needs an additional processing for the holes of a polygon --> polygon_parts[1] - polygon_parts[length-1]
//use only the outer ring
return polygon_parts[0];
//object for removed features
function remFeatures(removeCandidate, id_new_){;
this.id_new=id_new_; //this is the id of the feature, where this was aggregated to
this.type = undefined;
//object to store and administrate all removed features
function remFeatureStorage(){
this.removedFeatures = [];
var removedFeaturesIndizes = [];
function addRemFeat(remFeature_, id_new_){
var remFeature = new remFeatures(remFeature_, id_new_);
function getIndizes(){
removedFeaturesIndizes ={return});
return removedFeaturesIndizes;
//object to store and administrate all feature features
function featureObjStorage(){
this.featureObjects = [];
var featureObjectsIndizes = [];
function addFeatObj(feature){
var feature_obj = new featureObj(feature);
function getIndizes(){
featureObjectsIndizes ={return});
return featureObjectsIndizes;
function removeFeatObj(featureObject_, removedFeatureStorage_, addToCandidate_, indexOfMultiPolyPart__){
var originIndex_ = featureObjectsIndizes.indexOf(
var removed=this.featureObjects.splice(originIndex_,1)
//console.log("This is the storage of removed features ... is the new one here?! ",removedFeatureStorage_.removedFeatures)
else if(featureObject_.type=='MultiPolygon'){
featureObject_.removed_multi_parts.push(indexOfMultiPolyPart__) //!!!Maybe necessary to have an info within the featureObject that/which part of the multipolygon was removed
featureObject_.original_geometry[indexOfMultiPolyPart__] =
//console.log("control feature removement: ", featureObject_)
//End of 'Objects'
//Aggregation - 'Functions'
function aggregateFeatures(analysedFeature_, aggregationPartnerARC_, aggregationPartnerFeature_, arcCollection_, neighType_){
//change value of the arc_id of the aggregationPartnerARC, in dependence of it's direction as the analysedFeature needs the oposit of its aggregationPartner
if (aggregationPartnerARC_.direction == 1)var replace_id = aggregationPartnerARC_.arc_id;
else if (aggregationPartnerARC_.direction == -1)var replace_id = (aggregationPartnerARC_.arc_id+1)*(-1);
//differ between 'holes' and 'mesh_elements'
if(neighType_=='mesh_element'){ //features that are part of the topologic mesh
//reform the geometry of the analysed feature --> 'analysedFeature_'
var reformPolygonCache = reformPolygonArcs2(aggregationPartnerARC_.direction, aggregationPartnerARC_.arc_id, analysedFeature_);
//alocate the reformed and the original geometry to a variable
var reformedGeom = reformPolygonCache[0].slice();
var reformedGeomOrigin = reformPolygonCache[1]; //needed for final 'adding of holes'
//position and geometry finding depends of geometry type
//find the position ('arcIndex') of the aggregationPartnerARC in the geometry of the 'aggregationPartnerFeature'
var arcIndex = aggregationPartnerFeature_.geometry[0].indexOf(replace_id)
//!ToDo: Would be better to use the initial index that is comming with the neighboring arc!!!
//set the geometry of the aggregationPartner --> go on with that geometry
var aggregationPartnerGeometry = aggregationPartnerFeature_.geometry;
else {
//find the position ('arcIndex') of the aggregationPartnerARC by searching through all multipolygon-parts --> result: 'index__'
var indexOfReplace_id = aggregationPartnerFeature_.getArcsPure()[0].indexOf(replace_id)
if(indexOfReplace_id == -1)console.log("D'oh...the stupid problem happened cannot find the arc in the geometries!")
var index__ = aggregationPartnerFeature_.getArcsPure()[1][indexOfReplace_id][0]
//set the position ('arcIndex') of the aggregationPartnerARC in the detected multipolygon-part
var arcIndex = aggregationPartnerFeature_.getArcsPure()[1][indexOfReplace_id][2]
//!ToDo: Would be better to use the initial index that is comming with the neighboring arc!!!
//set the geometry of the multipolygon-part --> go on with that geometry
var aggregationPartnerGeometry = aggregationPartnerFeature_.geometry[index__];
//create the aggregated geometry by replacing the arc_id in the geometry of the aggregation partner with ... the reformed geometry of analysed feature
var new_composition = reCombinateArcs2(aggregationPartnerGeometry[0], arcIndex, reformedGeom) //(partner geometry, position of arc, reformed geometry of analysed feature)
//test for and remove redundant arcs, as they are not needed
new_composition = removeRedundantArcs3(new_composition);
//replace the corresponging geometry with the aggregated geometry
aggregationPartnerGeometry[0]=new_composition; //just replace the outer ring ... by that we simply keep the holes
//add the holes of the analysed feature to the aggregation partner...if some exist
//console.log("Hole pushing test_before: ",aggregationPartnerGeometry)
for(var i=1;i<reformedGeomOrigin.length;i++){
console.log("Test: ",i)
//console.log("Hole pushing test_after: ",aggregationPartnerGeometry)
else if (neighType_=='hole'){ //features, that are a hole in a polygon ... holes are not part of the topologic mesh
var multiPoly_holeIndex, multipoly_partIndex;
//console.log("Before hole remove: ", aggregationPartnerFeature_.geometry)
aggregationPartnerFeature_.geometry.forEach(function(part, j){
for(var i=1;i<part.length;i++){
if(part[i].indexOf(replace_id)!=-1) {multiPoly_holeIndex=i; multipoly_partIndex=j;}
var cache = aggregationPartnerFeature_.geometry[multipoly_partIndex].splice(multiPoly_holeIndex,1); //remove the hole from the 'aggregationPartner'
}else if (aggregationPartnerFeature_.type=='Polygon'){
for(var i=1;i<aggregationPartnerFeature_.geometry.length;i++){
if(aggregationPartnerFeature_.geometry[i].indexOf(replace_id)!=-1){ multiPoly_holeIndex=i;}
var cache = aggregationPartnerFeature_.geometry.splice(multiPoly_holeIndex,1); //remove the hole from the 'aggregationPartner'
console.log("Removed a hole from a feature...currently there is no method to finally store this removing!")
//console.log("After hole remove: ", aggregationPartnerFeature_.geometry)
} else console.log("For any reason...there is no neighbor definition given!")
aggregationPartnerFeature_.clipped=true; //let everybody know this feature (aggregationPartner) has aggregated another feature
aggregationPartnerFeature_.aggregatedFeatures.push( //...let them also know who was aggregated
return replace_id;
//function that removes the redundant arc from a feature's geometry and reforms it, when arc is surrounded by other arcs
function reformPolygonArcs2(direction, arc_id, feature){
if (direction == -1)var replace_id0 = arc_id;
else if (direction == 1)var replace_id0 = (arc_id+1)*(-1);
var found = false, x = 0, index__=-1;
if(feature.type=='Polygon')var arcs = feature.geometry;
else if (feature.type=='MultiPolygon') { //find the corresponding Polygon in the Multi-array
var arcIndex = feature.geometry[x][0].indexOf(replace_id0)
if(arcIndex!=-1){found=true; index__=x;}
var arcs = feature.geometry[index__];
return [editPolygon_(replace_id0, arcs),arcs];
function editPolygon_(id, arcs){
//get the indizes ...
var poly_index = 0, //poly_index=0--> we only need the outer ring
arc_index = arcs[poly_index].indexOf(id); //arc_index is the position of the arc in the array of the outer ring
if (arc_index == 0)var de_composition = arcs[poly_index].slice(1);
else { var de_composition = arcs[poly_index].slice(arc_index+1);
de_composition = de_composition.concat(arcs[poly_index].slice(0,arc_index));
//console.log("de_composition of arcs: ",de_composition)
return de_composition
//add the reformed ('redi_geom_') geometry to the -add to- geometry ('feature')
function reCombinateArcs2(feature, arc_index_, redi_geom_){
//when at position 0 --> [reformed arcs, original arcs {1...End}]
if (arc_index_ == 0){
var new_composition_ = redi_geom_;
new_composition_ = new_composition_.concat( feature.slice(1));
//when at any position X --> [original arcs {0...X-1}, reformed arcs, original arcs {X+1...End}]
}else { var new_composition_ = feature.slice(0,arc_index_);
new_composition_ = new_composition_.concat( redi_geom_);
new_composition_ = new_composition_.concat(feature.slice(arc_index_+1));
return new_composition_
//check for redundant arcs and remove them
//ToDO: I think this works not clean 100% ... you can see 'funny' holes when aggregated geometries surround a not-aggregated feature
function removeRedundantArcs3(new_composition_){
var new_composition__ = new_composition_.slice(0);
var valueArray= new Array();
for (var t=0;t<new_composition__.length;t++){
var this_arc = new_composition__[t];
var indexOfWord = getAllValues(valueArray).indexOf(this_arc);
var newValue = new data(this_arc, 1);
var allIndizes = []
valueArray.forEach(function(d){if(d.frequency>1)allIndizes=allIndizes.concat(d.indizes) })
//sort the found indizes of the found redundant arcs ... because ... delete them DESC, otherwise I would get problems with the indizes
allIndizes.sort(function sortfunction(a, b){return (b - a)})
//delete the redundant arcs by using the indizes
return new_composition__;
function data(value, frequency)
this.value = value;
this.frequency = frequency;
this.indizes = [];
function getAllValues(array_of_objects){
return{return d.value;})
//sort neighbors, in relation to the length of their arc
function sortArcs(neigh_arcs, arcCollection_){
var list ={
//a.length =arcCollection_[a.arc_id].length;
return a;})
list.sort(function sortfunction(a, b){return (arcCollection_[b.arc_id].length - arcCollection_[a.arc_id].length)})
//console.log('list-sorted: ', list)
return list;
//analyse the set of neighbors and return a 'neighborType'...possible values: isle, hole, mesh_element, undefined
function analyseNeighbors(neighbors_, type_, analysed_geometry_){
var neighType_ = undefined;
else if (neighbors_.length==1 && analysed_geometry_.length == 1 && type_=='MultiPolygon')neighType_='hole';
else {
var neighIDs = [], neighIDCounter = [];
//when it does not exist...push to arrays of IDs and IDCounters
if(neighIDs.indexOf({neighIDs.push(; neighIDCounter.push(1);}
//when it exists...inkrement the corresponding counter
else neighIDCounter[neighIDs.indexOf(]++;
//when there is only one id...this is also a hole
if(neighIDs.length==1 && analysed_geometry_.length == neighIDCounter.length && type_=='MultiPolygon')neighType_='hole';
else if(neighIDs.length==1 && type_=='Polygon')neighType_='mesh_element';
else if(neighIDs.length > 1)neighType_='mesh_element';
else if (neighIDs.length==1)neighType_='mesh_element';
else alert("Neighbor analysis throws an error!");
return neighType_;
//control if a feature exists and correct the feature-id of the neighbor, when it does not exist
function controlExistenceAndCorrect(id_origin, aggregationPartner_, arcCollection_, removedFeatureStorage_, sorted_neighbors_, featureObjects_, featuresObjectsIDs_, indexOfMultiPolyPart_, neighType_){
//Controll existence...loops until it found an existing feature (1.)...that is a polygon or an existing multipolygon-part (2.)...and has non-identic id with original feature (3.)
var exists = false, finished = false, aggPartID =, aggPartIndex = -1, id_cache =, endlessLoopAvoider = 0;
while (exists==false || finished==false){
//++++++++1.1. CHECK EXISTENCE OF AGGREGATION PARTNER+++++++++++++++
aggPartIndex = featuresObjectsIDs_.indexOf(aggPartID);
//++++++++1.2. REPLACE ID WITH ID OF AGGREGATED FEATURE+++++++++++++++
if(aggPartIndex==-1){ //does not exist in the array of 'allFeatures'
aggPartID = getNewID(removedFeatureStorage_, featuresObjectsIDs_, aggPartID); //this function has also a 'while'-loop integrated
aggPartIndex = featuresObjectsIDs_.indexOf(aggPartID) = aggPartID;
} else exists=true; //exists in the array of 'allFeatures' --> set boolean to 'true'
//++++++++GET THE COMPLETE NEIGHBOR FEATURE+++++++++++++++
var aggPartFeature = featureObjects_[aggPartIndex];
var arcID = aggregationPartner_.arc_id;
if(aggPartFeature.type=='Polygon')finished=true; //when it is a polygon --> directly go on!
else if(aggPartFeature.type=='MultiPolygon'){ //control if the the relevant part of the MultiPolygon does exist
var indexOfMultiPart = 1// = aggregationPartner_.index[0]; //the index of the multipoly-part is the first entry in the array
//1st of all...we have to find the right part of the multipolygon...we do this by evaluating each part
var testARCExistence = []; //-1 = number, 1 = object, without the searched arc, 0 = success, object with arc
if( typeof(part)=='number')testARCExistence.push(-1) //it is a number! - then it was replaced and the number indicates the id of the feature where it was aggregated to
else if( typeof(part)=='object'){ //it is an object! - good then we can search the corresponding arc in there
if(neighType_=='mesh_element') {
var arcIndex = part[0].indexOf(arcID);
if(arcIndex==-1)testARCExistence.push(1) //no...the arc is not within that part
else testARCExistence.push(0) //yes...the arc is within that part
}else if(neighType_=='hole'){
alert("Does this ever happen?")
var tester = false;
for(var j=1;j<part.length;j++){
var arcIndex = part[j].indexOf(arcID);
testARCExistence.push(0) //yes...the arc is within that part
if(tester == false)testARCExistence.push(1); //no...the arc is not within that part
//console.log("Finally: ", testARCExistence)
if(testARCExistence.indexOf(0)!=-1){ //cool...we found a multipoly-part that still exists and has the arc inside
indexOfMultiPart = testARCExistence.indexOf(0);
//console.log("Success! This is the right position: ",testARCExistence.indexOf(0),indexOfMultiPart)
else { //damn...we did not found a multipoly-part that still exists and has the arc inside
console.log("Oh no! All parts or the necessary one were replaced!")
var replacedParts = [];
//lets get all IDs of the already aggregated we have to search within them
var found_ = -1;
//loop through all of them...get the corresponding arcs...and find the searched arc
var featIndex = featuresObjectsIDs_.indexOf(d[0]);
if (featIndex==-1){ //nobody at home...this feature was already removed
var interID = getNewID(removedFeatureStorage_, featuresObjectsIDs_, d[0]); //get the new id
featIndex = featuresObjectsIDs_.indexOf(interID); //get the correct index using the new id
var feat = featureObjects_[featIndex]
if(found_!=-1)indexOfMultiPart=found_; //Yeah...we found it already at the next level
else { //Damn...we did not find it at the next level...we have to go deeper!
console.log("It is not enough to simply search for replaced have to implement a rekursive search!!!")
var aggPartFeature_MultiPart = aggPartFeature.original_geometry[indexOfMultiPart]; //get the corresponding part of the multipolygonal aggregation partner
//control existence of this part
if (typeof (aggPartFeature_MultiPart) == 'object')finished=true; //was not removed --> go on!
else if (typeof (aggPartFeature_MultiPart) == 'number'){ //was already removed_--> find new ID and begin the loop again
aggPartID = aggPartFeature_MultiPart; //replace the cached id = aggPartID; //replace the id of the 'aggregationPartner'
//console.log(" ",
else if (aggPartFeature_MultiPart == undefined) console.log("was not")
else console.log("Something went wrong within the 'control existence and correction of the aggregation partner!'")
//++++++++3. CHECK IF THE NEWLY FOUND ('aggPartID') ID IS DIFFERENT FROM THE ID OF THE ORIGINAL FEATURE ('id_origin')+++++++++++++++
console.log("Watchout! Identic ID's have been detected...maybe you don't like how it is currently implemented!!! Here they are: ", id_origin, aggPartID, id_cache)
var this_arc_members = arcCollection_[aggregationPartner_.arc_id].members;
//the other member of the arc, is the one we began to analyse at this we cached it at the beginning and use this one (id_cache) now
else if(this_arc_members[1].id!=id_cache)aggPartID=this_arc_members[1].id
//change the direction of the longest we are using now the other member of the arc
aggregationPartner_.direction = aggregationPartner_.direction * (-1) = aggPartID;
if(endlessLoopAvoider>=100){finished=true; aggregationPartner_ = undefined; console.log("Stopped the endless Loop in 'controlExistenceAndCorrect'!!!")}
return aggregationPartner_;
//get the new ID of a removed feature
function getNewID(removedFeatureStorage__, featuresObjectsIDs__, id__){
var stop_ = false, id = id__, endlessLoopAvoider = 0;
//console.log("intermediate steps1: ",removedFeatureStorage__.removedFeatures[removedFeatureStorage__.getIndizes().indexOf(id)].id_new)
id = removedFeatureStorage__.removedFeatures[removedFeatureStorage__.getIndizes().indexOf(id)].id_new;
//console.log("intermediate steps2: ",id)
var removedIndex_ = featuresObjectsIDs__.indexOf(id);
if(removedIndex_!=-1) stop_ = true; //the new one does exist ... else go on!
if(endlessLoopAvoider>=1000){stop_=true; console.log("Endless Loop in 'getNewID'!!!")}
return id;
function getNeighborCopy(neigh){
var aggregationPartner_ = {}; =;
aggregationPartner_.direction = neigh.direction;
aggregationPartner_.arc_id = neigh.arc_id;
aggregationPartner_.index = neigh.index;
return aggregationPartner_;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment