<commit_before>define.class("$ui/view", function(require,$ui$, view,label, $$, geo, urlfetch)
{		
	var KindSet = this.KindSet = {};
	var UnhandledKindSet = this.UnhandledKindSet = {};	
	var TileSize = 1024.0 ;
	this.attributes = {
		latlong:vec2(52.3608307,   4.8626387),
		centerx: 0, 
		centery: 0,
		zoomlevel: 16
	}
	
	
	function createHash (x, y, z){
			return x + "_" + y + "_" + z;
	}

		
	var landcolor1 = {farm:vec4(1,1,0.1,1), retail:vec4(0,0,1,0.5), tower:"white",library:"white",common:"white", sports_centre:"red", bridge:"gray", university:"red", breakwater:"blue", playground:"lime",forest:"darkgreen",pitch:"lime", grass:"#40d020", village_green:"green", garden:"green",residential:"gray" , footway:"gray", pedestrian:"gray", water:"#40a0ff",pedestrian:"lightgray", parking:"gray", park:"#40d030", earth:"lime", pier:"#404040", "rail" : vec4("purple"), "minor_road": vec4("orange"), "major_road" : vec4("red"), highway:vec4("black")}
	var landcolor2 = {farm:vec4(1,1,0.1,1), retail:vec4(0,0,1,0.5), tower:"gray", library:"gray", common:"gray", sports_centre:"white", bridge:"white", university:"black", breakwater:"blue", playground:"red", forest:"black",pitch:"green", grass:"green", village_green:"green", garden:"#40d080", residential:"lightgray" , footway:"yellow", pedestrian:"blue",water:"#f0ffff",pedestrian:"yellow", parking:"lightgray", park:"darkgreen", earth:"green", pier:"gray", "rail" : vec4("purple"), "minor_road": vec4("orange"), "major_road" : vec4("red"), highway:vec4("black")}
	//var landcolor1 ={water:"#40a0ff",park:"darkgreen", grass:"white" };
	
	var roadwidths = {water:20, path:2,ferry:4, "rail" : 4, "minor_road": 8, "major_road" : 12, path: 3, highway:12}
	var roadcolors = {water:"#30a0ff", path:"#d0d0d0", ferry:"lightblue", "rail" : vec4("purple"), "minor_road": vec4("#505050"), "major_road" : vec4("#404040"), highway:vec4("#303030")}
	var roadmarkcolors = {water:"#30a0ff", major_road:"white", minor_road:"#a0a0a0"}
			
	var landoffsets = {
		retail:27, 
		tower:26, 
		library:25, 
		common:24, 
		sports_centre:-23, 
		bridge:-22, 
		university:-21, 
		breakwater:-20, 
		playground:-19, 
		forest:-30,
		pitch:-17, 
		grass:-18, 
		village_green:-15, 
		garden:-14, 
		residential:-13, 
		footway:-12, 
		pedestrian:-11,
		water:-50,
		pedestrian:-9, 
		parking:-8, 
		park:-7, 
		earth:10, 
		pier:-5, 
		rail : -4,
		minor_road:-55, 
		major_road :-55, highway:-55
	}
	
	this.mouseleftdown = function(){
		console.log("mousedown");		
		this.mousemove = function(){
			console.log("dragging");
		}
	}
	
	this.mouseleftup = function(){
		console.log("up!");
		this.mousemove = function(){};
	}
	
	var earcut = require('$system/lib/earcut-port.js')().earcut;
	
	this.gotoCity = function(city, zoomlevel){
		this.dataset.gotoCity(city, zoomlevel);
	}
	
	var BuildingVertexStruct = define.struct({		
		pos:vec3,
		color1:vec4, 
		id: float,
		buildingid: float
	})
	
	var RoadVertexStruct = define.struct({		
		pos:vec3,
		color:vec4,
		side: float, 
		dist: float,
		linewidth:float,
		sidevec:vec2, 
		markcolor: vec4
	})
	
	var LandVertexStruct = define.struct({
		pos:vec3,
		color1:vec4,
		color2:vec4, 
		id: float
	})
	
	function arctotriangles(arc){
		if (!arc) return []
		var verts = []
		var flatverts = []
		var A0 = arc[0]
		var nx = A0[0]
		var ny = A0[1]

		flatverts.push(nx)
		flatverts.push(ny)
		
		for (var i = 1; i < arc.length; i++){
			var A = arc[i]
			nx += A[0]
			ny += A[1]
			flatverts.push(nx)
			flatverts.push(ny)
		}
		
		var triangles = earcut(flatverts)
		
		for(var i = 0; i < triangles.length; i++){
			idx = triangles[i]
			verts.push(vec2(flatverts[idx * 2], flatverts[idx * 2 + 1]))
		}

		return verts
	}

	
	function buildBuildingVertexBuffer(buildings){
		var mesh = BuildingVertexStruct.array();
		
		for(var i = 0;i<buildings.length;i++){
			var building = buildings[i];
			
			var theH = building.h;
			var isofac = 0 
			var isox = (theH*0.5)*isofac
			var isoy = (theH)*isofac
				
			if (building.arcs)
			for(var j = 0;j<building.arcs.length;j++){
				var arc = building.arcs[j];
				var tris = arctotriangles(arc);
				var A1 = vec2(arc[0][0], arc[0][1])
				var OA1 = A1;
				var c = 0.3;
				for(var a = 1;a<arc.length+1;a++)
				{
					var ca = arc[a%arc.length];
					
					var A2 = vec2(A1[0] + ca[0], A1[1] + ca[1]);
					if (a  == arc.length){
						A2[1] -= OA1[1];
						A2[0] -= OA1[0];
					}
					
					c = 0.4 + 0.3 *Math.sin(Math.atan2(A2[1]-A1[1], A2[0]-A1[0]));
					
					mesh.push(A1[0],A1[1],0, c,c,c, 1, i,building.id);
					mesh.push(A2[0],A2[1],0, c,c,c, 1, i,building.id);
					mesh.push(A2[0]+isox,A2[1]+isoy,theH, c,c,c, 1, i,building.id);
					mesh.push(A1[0],A1[1],0, c,c,c, 1, i,building.id);
					mesh.push(A2[0]+isox,A2[1]+isoy,theH, c,c,c, 1, i,building.id);
					mesh.push(A1[0]+isox,A1[1]+isoy,theH, c,c,c, 1, i,building.id);
					A1 = A2;
			
				}
				c = 0.4
				for(var a = 0;a<tris.length;a++){
					mesh.push(tris[a][0]+isox,tris[a][1]+isoy,theH, c,c,c, 1, i,building.id);
				}
			}							
		}
		return mesh;
	}
	
	function buildAreaPolygonVertexBuffer(areas, targetmesh){
		//console.log(areas);
		var mesh = targetmesh?targetmesh: LandVertexStruct.array();
		
		for(var i = 0;i<areas.length;i++){
			var off = 0;
			var land = areas[i];
			
			var color1 = vec4(1,0,1,1);
			var color2 = vec4(1,0,1,1);
			
			if (landcolor1[land.kind]) color1 = landcolor1[land.kind];else UnhandledKindSet[land.kind] = true;
			if (landcolor2[land.kind]) color2 = landcolor2[land.kind];else UnhandledKindSet[land.kind] = true;
			if (landoffsets[land.kind]) off = landoffsets[land.kind];
			
			if (land.arcs){
				for(var j = 0;j<land.arcs.length;j++){
					var arc = land.arcs[j];
					var tris = arctotriangles(arc);
					for(var a = 0;a<tris.length;a++){
						mesh.push(tris[a],off, vec4(color1), vec4(color2), i);
					}
				}
			}
		}
		return mesh;
	}

	function buildRoadPolygonVertexBuffer(roads){
		var mesh = RoadVertexStruct.array();
		var z = 10;
		for (var i = 0;i<roads.length;i++){							
					
					
	//						console.log(z);
					var R = roads[i];
					//console.log(R);
					var linewidth = 3;
					var color = vec4("gray") ;
					if (roadwidths[R.kind]) linewidth = roadwidths[R.kind];
					if (roadcolors[R.kind]) color = vec4(roadcolors[R.kind]);
					var markcolor = color;
					if (roadmarkcolors[R.kind]) markcolor = vec4(roadmarkcolors[R.kind]);
				
				//	linewidth *= Math.pow(2, this.view.zoomlevel-14);
					
					for(var rr = 0;rr<R.arcs.length;rr++){
						var currentarc = R.arcs[rr]
						if (currentarc.length == 1){
							continue
						}
						//	console.log(R, currentarc, currentarc.length, currentarc[0].length);
						
						//console.log(R, currentarc);
						//continue;
						var A0 = currentarc[0];
						var A1 = vec2(currentarc[1][0]+A0[0],currentarc[1][1]+A0[1]) ;
						
						//mesh.push(A0[0], A0[1], this.view.color);
						var nx = A0[0];
						var ny = A0[1];
						
						var odx = A1[0]-A0[0];
						var ody = A1[1]-A0[1];
						
						var predelta = vec2.normalize(vec2(odx, ody));
						var presdelta = vec2.rotate(predelta, 3.1415/2.0);
					
					
					
						var dist = 0;
						var dist2 = 0;
						var lastsdelta = vec2(0,0);
					//	color = vec4("blue");
						mesh.push(nx,ny,z, color, 1, dist,linewidth,presdelta, markcolor);
						mesh.push(nx,ny,z, color, -1, dist,linewidth,presdelta, markcolor);

						mesh.push(nx - predelta[0]*linewidth*0.5,ny - predelta[1]*linewidth*0.5,z, color, 0.5, -10 ,linewidth,presdelta, markcolor);

						mesh.push(nx - predelta[0]*linewidth*0.5,ny - predelta[1]*linewidth*0.5,z, color, 0.5, -10 ,linewidth,presdelta, markcolor);
						mesh.push(nx - predelta[0]*linewidth*0.5,ny - predelta[1]*linewidth*0.5,z, color, -0.5, -10 ,linewidth,presdelta, markcolor);

						//mesh.push(nx,ny, color, 1, dist,linewidth,presdelta, markcolor);
						mesh.push(nx,ny, z, color, -1, dist,linewidth,presdelta, markcolor);


					//	color = vec4(0,0,0.03,0.1)
					var lastdelta = vec2(0);
						for(var a = 1;a<currentarc.length;a++){					
							var A =currentarc[a];
							
							var tnx = nx + A[0];
							var tny = ny + A[1];
							var predelt = vec2( tnx - nx, tny - ny);
							var delta = vec2.normalize(predelt);
							var sdelta = vec2.rotate(delta, PI/2);
					
							var dist2 = dist +  vec2.len(predelt);
							
							if (a>1){
								mesh.push(nx,ny,z, color, 1, dist,linewidth,lastsdelta, markcolor);
								mesh.push(nx,ny,z, color, 1, dist,linewidth,sdelta, markcolor);
								mesh.push(nx,ny,z, color, -1, dist,linewidth,sdelta, markcolor);
								
								mesh.push(nx,ny,z, color, 1, dist,linewidth,lastsdelta, markcolor);
								mesh.push(nx,ny,z, color,-1, dist,linewidth,sdelta, markcolor);
								mesh.push(nx,ny,z, color, -1, dist,linewidth,lastsdelta, markcolor);
									
							}
							//color = vec4(0,1,0,0.2)
							mesh.push( nx, ny,z,color, 1, dist ,linewidth, sdelta, markcolor);
							mesh.push( nx, ny,z,color,-1, dist ,linewidth, sdelta, markcolor);
							mesh.push(tnx,tny,z,color, 1, dist2,linewidth, sdelta, markcolor);
							
							mesh.push(nx,ny,z,color,-1, dist,linewidth, sdelta, markcolor);
							mesh.push(tnx,tny,z,color,1,dist2,linewidth, sdelta, markcolor);
							mesh.push(tnx,tny,z,color,-1, dist2,linewidth, sdelta, markcolor);
							
							lastsdelta = vec2(sdelta[0], sdelta[1]);
							dist = dist2;									
							nx = tnx;
							ny = tny;
							lastdelta = delta;
						}
						//color = vec4("red");
						mesh.push(nx,ny,z, color, 1, dist,linewidth,lastsdelta, markcolor);
						mesh.push(nx,ny,z, color, -1, dist,linewidth,lastsdelta, markcolor);
						mesh.push(nx + lastdelta[0]*linewidth*0.5,ny + lastdelta[1]*linewidth*0.5,z, color, 0.5, dist+linewidth*0.5 ,linewidth,lastsdelta, markcolor);

						mesh.push(nx + lastdelta[0]*linewidth*0.5,ny + lastdelta[1]*linewidth*0.5,z, color, 0.5, dist+linewidth*0.5 ,linewidth,lastsdelta, markcolor);
						mesh.push(nx + lastdelta[0]*linewidth*0.5,ny + lastdelta[1]*linewidth*0.5,z, color, -0.5, dist+linewidth*0.5,linewidth,lastsdelta, markcolor);
						mesh.push(nx,ny, z,color, -1, dist,linewidth,presdelta, markcolor);

					}
				}
				
		return mesh;
		
	}

	define.class(this, "building", function($ui$, view){
		
		this.attributes = {
			buildings: [],
			scalefactor: 1.0,
			currentbuilding: -1,
			currentbuildingid: -1,
			vertexbuffer: []
		}

		this.boundscheck = false
		
		this.onbuildings = function(){
			this.pickrange = this.buildings.length
			//console.log("setting pickrange:", this.pickrange);
		}
		
		this.mouseout = function(){
			this.currentbuilding = -1;
			this.currentbuildingid = -1;
		}

		this.mouseover =  function(){
			var building = this.buildings[this.last_pick_id]
			this.currentbuilding = this.last_pick_id
			if(building){
				this.currentbuildingid = building.id;
				console.log(this.currentbuildingid, building.id, building);
				
				var text = "Building"
				//console.log(building);
				if(building.kind) text += " " + building.kind
				if(building.name) text += " " + building.name
				if(building.street) text += " " + building.street
				if(building.housenumber) text += " " + building.housenumber
				this.screen.status = text
			}
			else {
				this.currentbuildingid = -1;
			
				console.log(this.last_pick_id)
			}
		}
		
		this.bg = function(){
			
			this.vertexstruct =  BuildingVertexStruct;

			this.mesh = this.vertexstruct.array();
			this.color = function(){

				PickGuid = mesh.id;
				if (abs(view.currentbuilding - mesh.id)<0.2) return vec4(mesh.color1.x, 0, 0, 1);
				if (abs(view.currentbuildingid - mesh.buildingid)<0.2) return vec4(mesh.color1.x * 0.8, 0, 0, 1);
				//return pal.pal1(mesh.pos.z/300.-0.1*view.time +mesh.id/100.) * mesh.color
				return mesh.color1;
			}
	
			this.update = function(){
				this.mesh = this.view.vertexbuffer;
				
				
			}
			
			this.position = function(){					
				return vec4(mesh.pos.x, 1000-mesh.pos.y, mesh.pos.z, 1) * view.totalmatrix * view.viewmatrix
			}
			
			this.drawtype = this.TRIANGLES
			this.linewidth = 4
		
		}
	})
				
	define.class(this, "land", function($ui$, view){
		this.boundscheck = false
		
		this.attributes = {
			lands:[],
			currentland: -1,
			vertexbuffer: []
		}
		
		this.mouseover =  function(evt){
			//console.log(this.last_pick_id)
			this.currentland = this.last_pick_id ;
			var text = "Land: " + this.lands[this.last_pick_id ].kind;
			this.screen.status = text;				
		}	
		
		this.mouseout = function(){
			this.currentland = -1;
		}
		
		this.onlands = function(){
			this.pickrange = this.lands.length;
		}
		
		this.bg = function(){
						
			
			this.vertexstruct =  LandVertexStruct;
			
			
			this.mesh = this.vertexstruct.array();
			
			this.color = function(){						
				var xy = vec2(mesh.pos.xy)*0.2
				//var n1 = (noise.noise2d(xy))*0.25 + 0.25;
				//var n2 = 0.8*noise.noise2d(xy*2.3)
				var themix = 0.5
				PickGuid = mesh.id
				if (abs(view.currentland - mesh.id)<0.2) return "red";
				//mod(mesh.id, 256.)
				//PickGuid.y = floor(mesh.id/256.)
				return mix(mesh.color1, mesh.color2,themix);						
			}
	
			this.update = function(){
				this.mesh = this.view.vertexbuffer;
				
			}
			
			this.position = function(){					
				var r = vec4(mesh.pos.x, 1000-mesh.pos.y, 0, 1) * view.totalmatrix * view.viewmatrix;
				r.w -= mesh.pos.z*0.01;
				return r
			}
				
			this.drawtype = this.TRIANGLES
			this.linewidth = 4
		}
	})
	
	define.class(this, "road", function($ui$, view){
		this.boundscheck = false;
		
		this.attributes = {					
			roads:[],
			zoomlevel: 16,
			zoomscale: 2.0
		}
		this.bg = function(){		
			this.vertexstruct =  RoadVertexStruct;
			
			this.mesh = this.vertexstruct.array();
			
			this.color = function(){
				if (abs(mesh.side) > 0.85) return mix("black", mesh.color, 0.8)
				return mesh.color;
			}
			
			this.widths = {water:20, path:2,ferry:4, "rail" : 4, "minor_road": 8, "major_road" : 12, path: 3, highway:12}
			this.colors = {water:"#30a0ff", path:"#d0d0d0", ferry:"lightblue", "rail" : vec4("purple"), "minor_road": vec4("#505050"), "major_road" : vec4("#404040"), highway:vec4("#303030")}
			this.markcolors = {water:"#30a0ff", major_road:"white", minor_road:"#a0a0a0"}
		
			this.update = function(){
				//console.log("updating");
				this.mesh = this.vertexstruct.array();
				var 	z = 0.1;

				
			}
			
			this.position = function(){					
				var pos = mesh.pos.xy + mesh.sidevec * mesh.side * view.zoomscale*mesh.linewidth*0.5;
				var res = vec4(pos.x, 1000-pos.y, 0, 1.0) * view.totalmatrix * view.viewmatrix;
				res.w += mesh.pos.z;
				return res
			}
			
			this.drawtype = this.TRIANGLES
			this.linewidth = 4;				
		}
	})
		
	define.class(this, "mapdataset", "$system/base/node", function( $$, geo)
	{
		this.requestPending = false;
		this.loadqueue = [];
		this.loadqueuehash = [];
		this.loadedblocks = {};
		
		var geo = this.geo = geo();
		
		this.attributes = {
			centerx: 0,
			centery: 0,
			latlong: vec2(52.3608307,   4.8626387),
			zoomlevel: 15,
			callbacktarget: {}
		}
		
		this.setCenterLatLng = function(lat, lng, zoom, time){	
			zoom = Math.floor(zoom);
			var llm = geo.latLngToMeters(lat, lng)
			var tvm = geo.tileForMeters(llm[0], llm[1], zoom );
			this.setCenter(tvm.x , tvm.y , zoom, time);
		}
		
		this.setCenter = function(x,y,z, time){
			
			if (!time || time == 0)
			{
				this.centerx = x;
				this.centery = y;
				this.zoomlevel = z;
				for(var xx = -5; xx < 5; xx++){
					for(var yy = -5; yy < 5; yy++){			
						this.addToQueue(x + xx,y + yy,z);	
					}
				}					
			}
			else{
				console.log("todo: set up animation");
				debugger;
			}
		}
		

		this.addToQueue = function(x, y, z){		
			var hash = createHash(x, y, z);
			if (this.loadqueuehash[hash]) return; // already queued for load.
			if (this.loadedblocks[hash]) return; // already loaded.
			
			this.loadqueuehash[hash] = 1;
			this.loadqueue.push({x:x, y:y, z:z});
			this.updateLoadQueue();
		}

		this.processLoadedBlock = function(x, y, z, data){
			var hash = createHash(x,y,z);
			this.loadedblocks[hash] = {x:x, y:y, z:z, blockdata:data};
		}
		
		this.simulateLoaded = function(){
			if (this.currentRequest) {
				
				var r = this.currentRequest;
				var hash = createHash(r.x, r.y, r.z);
				this.loadedblocks[hash] = this.currentRequest
				this.loadqueuehash[hash] =  undefined;
				this.currentRequest = undefined;
				this.cleanLoadedBlocks();
				this.updateLoadQueue();
			}		
		}
		
		this.cleanLoadedBlocks = function(){
			var keys = Object.keys(this.loadedblocks);
			if (keys.length < 150) return;
			for(var i =0 ;i<keys.length;i++)
			{
				var lb = this.loadedblocks[keys[i]]
				var dx = lb.x - this.centerx;
				var dy = lb.y - this.centery;
				var dz = (lb.z - this.zoomlevel)*25;
				var dist  = dx*dx + dy*dy + dz*dz;
				if (dist > 5*5*5*5) {
					delete this.loadedblocks[keys[i]];
				}
			}
		}
		
		this.loadstring = function(str){
			if (this.currentRequest) {
					var Bset = [];
					var Rset = [];
					var Wset = [];
					var Eset = [];
					var Lset = [];
					var Allset = [];<commit_msg>Building mesh generator optimizations<commit_after>define.class("$ui/view", function(require,$ui$, view,label, $$, geo, urlfetch)
{		
	var KindSet = this.KindSet = {};
	var UnhandledKindSet = this.UnhandledKindSet = {};	
	var TileSize = 1024.0 ;
	this.attributes = {
		latlong:vec2(52.3608307,   4.8626387),
		centerx: 0, 
		centery: 0,
		zoomlevel: 16
	}
	
	
	function createHash (x, y, z){
			return x + "_" + y + "_" + z;
	}

		
	var landcolor1 = {farm:vec4(1,1,0.1,1), retail:vec4(0,0,1,0.5), tower:"white",library:"white",common:"white", sports_centre:"red", bridge:"gray", university:"red", breakwater:"blue", playground:"lime",forest:"darkgreen",pitch:"lime", grass:"#40d020", village_green:"green", garden:"green",residential:"gray" , footway:"gray", pedestrian:"gray", water:"#40a0ff",pedestrian:"lightgray", parking:"gray", park:"#40d030", earth:"lime", pier:"#404040", "rail" : vec4("purple"), "minor_road": vec4("orange"), "major_road" : vec4("red"), highway:vec4("black")}
	var landcolor2 = {farm:vec4(1,1,0.1,1), retail:vec4(0,0,1,0.5), tower:"gray", library:"gray", common:"gray", sports_centre:"white", bridge:"white", university:"black", breakwater:"blue", playground:"red", forest:"black",pitch:"green", grass:"green", village_green:"green", garden:"#40d080", residential:"lightgray" , footway:"yellow", pedestrian:"blue",water:"#f0ffff",pedestrian:"yellow", parking:"lightgray", park:"darkgreen", earth:"green", pier:"gray", "rail" : vec4("purple"), "minor_road": vec4("orange"), "major_road" : vec4("red"), highway:vec4("black")}
	//var landcolor1 ={water:"#40a0ff",park:"darkgreen", grass:"white" };
	
	var roadwidths = {water:20, path:2,ferry:4, "rail" : 4, "minor_road": 8, "major_road" : 12, path: 3, highway:12}
	var roadcolors = {water:"#30a0ff", path:"#d0d0d0", ferry:"lightblue", "rail" : vec4("purple"), "minor_road": vec4("#505050"), "major_road" : vec4("#404040"), highway:vec4("#303030")}
	var roadmarkcolors = {water:"#30a0ff", major_road:"white", minor_road:"#a0a0a0"}
			
	var landoffsets = {
		retail:27, 
		tower:26, 
		library:25, 
		common:24, 
		sports_centre:-23, 
		bridge:-22, 
		university:-21, 
		breakwater:-20, 
		playground:-19, 
		forest:-30,
		pitch:-17, 
		grass:-18, 
		village_green:-15, 
		garden:-14, 
		residential:-13, 
		footway:-12, 
		pedestrian:-11,
		water:-50,
		pedestrian:-9, 
		parking:-8, 
		park:-7, 
		earth:10, 
		pier:-5, 
		rail : -4,
		minor_road:-55, 
		major_road :-55, highway:-55
	}
	
	this.mouseleftdown = function(){
		console.log("mousedown");		
		this.mousemove = function(){
			console.log("dragging");
		}
	}
	
	this.mouseleftup = function(){
		console.log("up!");
		this.mousemove = function(){};
	}
	
	var earcut = require('$system/lib/earcut-port.js')().earcut;
	
	this.gotoCity = function(city, zoomlevel){
		this.dataset.gotoCity(city, zoomlevel);
	}
	
	var BuildingVertexStruct = define.struct({		
		pos:vec3,
		color1:vec4, 
		id: float,
		buildingid: float
	})
	
	var RoadVertexStruct = define.struct({		
		pos:vec3,
		color:vec4,
		side: float, 
		dist: float,
		linewidth:float,
		sidevec:vec2, 
		markcolor: vec4
	})
	
	var LandVertexStruct = define.struct({
		pos:vec3,
		color1:vec4,
		color2:vec4, 
		id: float
	})
	
	function arctotriangles(arc){
		if (!arc) return []
		var verts = []
		var flatverts = []
		var A0 = arc[0]
		var nx = A0[0]
		var ny = A0[1]

		flatverts.push(nx)
		flatverts.push(ny)
		
		for (var i = 1; i < arc.length; i++){
			var A = arc[i]
			nx += A[0]
			ny += A[1]
			flatverts.push(nx)
			flatverts.push(ny)
		}
		
		var triangles = earcut(flatverts)
		
		for(var i = 0; i < triangles.length; i++){
			idx = triangles[i]
			verts.push([flatverts[idx * 2], flatverts[idx * 2 + 1]])
		}

		return verts
	}

	
	function buildBuildingVertexBuffer(buildings){
		var mesh = BuildingVertexStruct.array(buildings.length * 30);
		
		for(var i = 0;i<buildings.length;i++){
			var building = buildings[i];
			
			var theH = building.h;
			var isofac = 0 
			var isox = (theH*0.5)*isofac
			var isoy = (theH)*isofac
			var bid = building.id;
				
			if (building.arcs)
			for(var j = 0;j<building.arcs.length;j++){
				var arc = building.arcs[j];
				
				var tris = arctotriangles(arc);
				var A1 = [arc[0][0], arc[0][1]]
				var OA1 = A1;
				var c = 0.3;
				for(var a = 1;a<arc.length+1;a++)
				{
					var ca = arc[a%arc.length];
					
					var A2 = [A1[0] + ca[0], A1[1] + ca[1]];
					if (a  == arc.length){
						A2[1] -= OA1[1];
						A2[0] -= OA1[0];
					}
					
					c = 0.4 + 0.3 *Math.sin(Math.atan2(A2[1]-A1[1], A2[0]-A1[0]));
					mesh.push(A1[0],A1[1],0, c,c,c, 1, i,bid);
					mesh.push(A2[0],A2[1],0, c,c,c, 1, i,bid);
					mesh.push(A2[0]+isox,A2[1]+isoy,theH, c,c,c, 1, i,bid);
					mesh.push(A1[0],A1[1],0, c,c,c, 1, i,bid);
					mesh.push(A2[0]+isox,A2[1]+isoy,theH, c,c,c, 1, i,bid);
					mesh.push(A1[0]+isox,A1[1]+isoy,theH, c,c,c, 1, i,bid);
					A1 = A2;
			
				}
				c = 0.4
				for(var a = 0;a<tris.length;a++){
					var atri = tris[a];
					mesh.push(atri[0]+isox,atri[1]+isoy,theH, c,c,c, 1, i,bid);
				}
			}							
		}
		//if (!window.teller) window.teller = 0;
		//window.teller += mesh.length * 40;
		return mesh;
	}
	
	function buildAreaPolygonVertexBuffer(areas, targetmesh){
		//console.log(areas);
		var mesh = targetmesh?targetmesh: LandVertexStruct.array();
		
		for(var i = 0;i<areas.length;i++){
			var off = 0;
			var land = areas[i];
			
			var color1 = vec4(1,0,1,1);
			var color2 = vec4(1,0,1,1);
			
			if (landcolor1[land.kind]) color1 = landcolor1[land.kind];else UnhandledKindSet[land.kind] = true;
			if (landcolor2[land.kind]) color2 = landcolor2[land.kind];else UnhandledKindSet[land.kind] = true;
			if (landoffsets[land.kind]) off = landoffsets[land.kind];
			
			if (land.arcs){
				for(var j = 0;j<land.arcs.length;j++){
					var arc = land.arcs[j];
					var tris = arctotriangles(arc);
					for(var a = 0;a<tris.length;a++){
						mesh.push(tris[a],off, vec4(color1), vec4(color2), i);
					}
				}
			}
		}
		return mesh;
	}

	function buildRoadPolygonVertexBuffer(roads){
		var mesh = RoadVertexStruct.array();
		var z = 10;
		for (var i = 0;i<roads.length;i++){							
					
					
	//						console.log(z);
					var R = roads[i];
					//console.log(R);
					var linewidth = 3;
					var color = vec4("gray") ;
					if (roadwidths[R.kind]) linewidth = roadwidths[R.kind];
					if (roadcolors[R.kind]) color = vec4(roadcolors[R.kind]);
					var markcolor = color;
					if (roadmarkcolors[R.kind]) markcolor = vec4(roadmarkcolors[R.kind]);
				
				//	linewidth *= Math.pow(2, this.view.zoomlevel-14);
					
					for(var rr = 0;rr<R.arcs.length;rr++){
						var currentarc = R.arcs[rr]
						if (currentarc.length == 1){
							continue
						}
						//	console.log(R, currentarc, currentarc.length, currentarc[0].length);
						
						//console.log(R, currentarc);
						//continue;
						var A0 = currentarc[0];
						var A1 = vec2(currentarc[1][0]+A0[0],currentarc[1][1]+A0[1]) ;
						
						//mesh.push(A0[0], A0[1], this.view.color);
						var nx = A0[0];
						var ny = A0[1];
						
						var odx = A1[0]-A0[0];
						var ody = A1[1]-A0[1];
						
						var predelta = vec2.normalize(vec2(odx, ody));
						var presdelta = vec2.rotate(predelta, 3.1415/2.0);
					
					
					
						var dist = 0;
						var dist2 = 0;
						var lastsdelta = vec2(0,0);
					//	color = vec4("blue");
						mesh.push(nx,ny,z, color, 1, dist,linewidth,presdelta, markcolor);
						mesh.push(nx,ny,z, color, -1, dist,linewidth,presdelta, markcolor);

						mesh.push(nx - predelta[0]*linewidth*0.5,ny - predelta[1]*linewidth*0.5,z, color, 0.5, -10 ,linewidth,presdelta, markcolor);

						mesh.push(nx - predelta[0]*linewidth*0.5,ny - predelta[1]*linewidth*0.5,z, color, 0.5, -10 ,linewidth,presdelta, markcolor);
						mesh.push(nx - predelta[0]*linewidth*0.5,ny - predelta[1]*linewidth*0.5,z, color, -0.5, -10 ,linewidth,presdelta, markcolor);

						//mesh.push(nx,ny, color, 1, dist,linewidth,presdelta, markcolor);
						mesh.push(nx,ny, z, color, -1, dist,linewidth,presdelta, markcolor);


					//	color = vec4(0,0,0.03,0.1)
					var lastdelta = vec2(0);
						for(var a = 1;a<currentarc.length;a++){					
							var A =currentarc[a];
							
							var tnx = nx + A[0];
							var tny = ny + A[1];
							var predelt = vec2( tnx - nx, tny - ny);
							var delta = vec2.normalize(predelt);
							var sdelta = vec2.rotate(delta, PI/2);
					
							var dist2 = dist +  vec2.len(predelt);
							
							if (a>1){
								mesh.push(nx,ny,z, color, 1, dist,linewidth,lastsdelta, markcolor);
								mesh.push(nx,ny,z, color, 1, dist,linewidth,sdelta, markcolor);
								mesh.push(nx,ny,z, color, -1, dist,linewidth,sdelta, markcolor);
								
								mesh.push(nx,ny,z, color, 1, dist,linewidth,lastsdelta, markcolor);
								mesh.push(nx,ny,z, color,-1, dist,linewidth,sdelta, markcolor);
								mesh.push(nx,ny,z, color, -1, dist,linewidth,lastsdelta, markcolor);
									
							}
							//color = vec4(0,1,0,0.2)
							mesh.push( nx, ny,z,color, 1, dist ,linewidth, sdelta, markcolor);
							mesh.push( nx, ny,z,color,-1, dist ,linewidth, sdelta, markcolor);
							mesh.push(tnx,tny,z,color, 1, dist2,linewidth, sdelta, markcolor);
							
							mesh.push(nx,ny,z,color,-1, dist,linewidth, sdelta, markcolor);
							mesh.push(tnx,tny,z,color,1,dist2,linewidth, sdelta, markcolor);
							mesh.push(tnx,tny,z,color,-1, dist2,linewidth, sdelta, markcolor);
							
							lastsdelta = vec2(sdelta[0], sdelta[1]);
							dist = dist2;									
							nx = tnx;
							ny = tny;
							lastdelta = delta;
						}
						//color = vec4("red");
						mesh.push(nx,ny,z, color, 1, dist,linewidth,lastsdelta, markcolor);
						mesh.push(nx,ny,z, color, -1, dist,linewidth,lastsdelta, markcolor);
						mesh.push(nx + lastdelta[0]*linewidth*0.5,ny + lastdelta[1]*linewidth*0.5,z, color, 0.5, dist+linewidth*0.5 ,linewidth,lastsdelta, markcolor);

						mesh.push(nx + lastdelta[0]*linewidth*0.5,ny + lastdelta[1]*linewidth*0.5,z, color, 0.5, dist+linewidth*0.5 ,linewidth,lastsdelta, markcolor);
						mesh.push(nx + lastdelta[0]*linewidth*0.5,ny + lastdelta[1]*linewidth*0.5,z, color, -0.5, dist+linewidth*0.5,linewidth,lastsdelta, markcolor);
						mesh.push(nx,ny, z,color, -1, dist,linewidth,presdelta, markcolor);

					}
				}
				
		return mesh;
		
	}

	define.class(this, "building", function($ui$, view){
		
		this.attributes = {
			buildings: [],
			scalefactor: 1.0,
			currentbuilding: -1,
			currentbuildingid: -1,
			vertexbuffer: []
		}

		this.boundscheck = false
		
		this.onbuildings = function(){
			this.pickrange = this.buildings.length
			//console.log("setting pickrange:", this.pickrange);
		}
		
		this.mouseout = function(){
			this.currentbuilding = -1;
			this.currentbuildingid = -1;
		}

		this.mouseover =  function(){
			var building = this.buildings[this.last_pick_id]
			this.currentbuilding = this.last_pick_id
			if(building){
				this.currentbuildingid = building.id;
				console.log(this.currentbuildingid, building.id, building);
				
				var text = "Building"
				//console.log(building);
				if(building.kind) text += " " + building.kind
				if(building.name) text += " " + building.name
				if(building.street) text += " " + building.street
				if(building.housenumber) text += " " + building.housenumber
				this.screen.status = text
			}
			else {
				this.currentbuildingid = -1;
			
				console.log(this.last_pick_id)
			}
		}
		
		this.bg = function(){
			
			this.vertexstruct =  BuildingVertexStruct;

			this.mesh = this.vertexstruct.array();
			this.color = function(){

				PickGuid = mesh.id;
				if (abs(view.currentbuilding - mesh.id)<0.2) return vec4(mesh.color1.x, 0, 0, 1);
				if (abs(view.currentbuildingid - mesh.buildingid)<0.2) return vec4(mesh.color1.x * 0.8, 0, 0, 1);
				//return pal.pal1(mesh.pos.z/300.-0.1*view.time +mesh.id/100.) * mesh.color
				return mesh.color1;
			}
	
			this.update = function(){
				this.mesh = this.view.vertexbuffer;
				
				
			}
			
			this.position = function(){					
				return vec4(mesh.pos.x, 1000-mesh.pos.y, mesh.pos.z, 1) * view.totalmatrix * view.viewmatrix
			}
			
			this.drawtype = this.TRIANGLES
			this.linewidth = 4
		
		}
	})
				
	define.class(this, "land", function($ui$, view){
		this.boundscheck = false
		
		this.attributes = {
			lands:[],
			currentland: -1,
			vertexbuffer: []
		}
		
		this.mouseover =  function(evt){
			//console.log(this.last_pick_id)
			this.currentland = this.last_pick_id ;
			var text = "Land: " + this.lands[this.last_pick_id ].kind;
			this.screen.status = text;				
		}	
		
		this.mouseout = function(){
			this.currentland = -1;
		}
		
		this.onlands = function(){
			this.pickrange = this.lands.length;
		}
		
		this.bg = function(){
						
			
			this.vertexstruct =  LandVertexStruct;
			
			
			this.mesh = this.vertexstruct.array();
			
			this.color = function(){						
				var xy = vec2(mesh.pos.xy)*0.2
				//var n1 = (noise.noise2d(xy))*0.25 + 0.25;
				//var n2 = 0.8*noise.noise2d(xy*2.3)
				var themix = 0.5
				PickGuid = mesh.id
				if (abs(view.currentland - mesh.id)<0.2) return "red";
				//mod(mesh.id, 256.)
				//PickGuid.y = floor(mesh.id/256.)
				return mix(mesh.color1, mesh.color2,themix);						
			}
	
			this.update = function(){
				this.mesh = this.view.vertexbuffer;
				
			}
			
			this.position = function(){					
				var r = vec4(mesh.pos.x, 1000-mesh.pos.y, 0, 1) * view.totalmatrix * view.viewmatrix;
				r.w -= mesh.pos.z*0.01;
				return r
			}
				
			this.drawtype = this.TRIANGLES
			this.linewidth = 4
		}
	})
	
	define.class(this, "road", function($ui$, view){
		this.boundscheck = false;
		
		this.attributes = {					
			roads:[],
			zoomlevel: 16,
			zoomscale: 2.0
		}
		this.bg = function(){		
			this.vertexstruct =  RoadVertexStruct;
			
			this.mesh = this.vertexstruct.array();
			
			this.color = function(){
				if (abs(mesh.side) > 0.85) return mix("black", mesh.color, 0.8)
				return mesh.color;
			}
			
			this.widths = {water:20, path:2,ferry:4, "rail" : 4, "minor_road": 8, "major_road" : 12, path: 3, highway:12}
			this.colors = {water:"#30a0ff", path:"#d0d0d0", ferry:"lightblue", "rail" : vec4("purple"), "minor_road": vec4("#505050"), "major_road" : vec4("#404040"), highway:vec4("#303030")}
			this.markcolors = {water:"#30a0ff", major_road:"white", minor_road:"#a0a0a0"}
		
			this.update = function(){
				//console.log("updating");
				this.mesh = this.vertexstruct.array();
				var 	z = 0.1;

				
			}
			
			this.position = function(){					
				var pos = mesh.pos.xy + mesh.sidevec * mesh.side * view.zoomscale*mesh.linewidth*0.5;
				var res = vec4(pos.x, 1000-pos.y, 0, 1.0) * view.totalmatrix * view.viewmatrix;
				res.w += mesh.pos.z;
				return res
			}
			
			this.drawtype = this.TRIANGLES
			this.linewidth = 4;				
		}
	})
		
	define.class(this, "mapdataset", "$system/base/node", function( $$, geo)
	{
		this.requestPending = false;
		this.loadqueue = [];
		this.loadqueuehash = [];
		this.loadedblocks = {};
		
		var geo = this.geo = geo();
		
		this.attributes = {
			centerx: 0,
			centery: 0,
			latlong: vec2(52.3608307,   4.8626387),
			zoomlevel: 15,
			callbacktarget: {}
		}
		
		this.setCenterLatLng = function(lat, lng, zoom, time){	
			zoom = Math.floor(zoom);
			var llm = geo.latLngToMeters(lat, lng)
			var tvm = geo.tileForMeters(llm[0], llm[1], zoom );
			this.setCenter(tvm.x , tvm.y , zoom, time);
		}
		
		this.setCenter = function(x,y,z, time){
			
			if (!time || time == 0)
			{
				this.centerx = x;
				this.centery = y;
				this.zoomlevel = z;
				for(var xx = -5; xx < 5; xx++){
					for(var yy = -5; yy < 5; yy++){			
						this.addToQueue(x + xx,y + yy,z);	
					}
				}					
			}
			else{
				console.log("todo: set up animation");
				debugger;
			}
		}
		

		this.addToQueue = function(x, y, z){		
			var hash = createHash(x, y, z);
			if (this.loadqueuehash[hash]) return; // already queued for load.
			if (this.loadedblocks[hash]) return; // already loaded.
			
			this.loadqueuehash[hash] = 1;
			this.loadqueue.push({x:x, y:y, z:z});
			this.updateLoadQueue();
		}

		this.processLoadedBlock = function(x, y, z, data){
			var hash = createHash(x,y,z);
			this.loadedblocks[hash] = {x:x, y:y, z:z, blockdata:data};
		}
		
		this.simulateLoaded = function(){
			if (this.currentRequest) {
				
				var r = this.currentRequest;
				var hash = createHash(r.x, r.y, r.z);
				this.loadedblocks[hash] = this.currentRequest
				this.loadqueuehash[hash] =  undefined;
				this.currentRequest = undefined;
				this.cleanLoadedBlocks();
				this.updateLoadQueue();
			}		
		}
		
		this.cleanLoadedBlocks = function(){
			var keys = Object.keys(this.loadedblocks);
			if (keys.length < 150) return;
			for(var i =0 ;i<keys.length;i++)
			{
				var lb = this.loadedblocks[keys[i]]
				var dx = lb.x - this.centerx;
				var dy = lb.y - this.centery;
				var dz = (lb.z - this.zoomlevel)*25;
				var dist  = dx*dx + dy*dy + dz*dz;
				if (dist > 5*5*5*5) {
					delete this.loadedblocks[keys[i]];
				}
			}
		}
		
		this.loadstring = function(str){
		//	if (!window.teller) window.teller = 0;
		//	window.teller += str.length;
			
			if (this.currentRequest) {
					var Bset = [];
					var Rset = [];
					var Wset = [];
					var Eset = [];
					var Lset = [];
					var Allset = [];
{'fraction_non_alphanumeric': 0.1779461719362425, 'fraction_numerical': 0.03974682809279099, 'mean_word_length': 2.959080459770115, 'pattern_counts': {'":': 12, '<': 41, '<?xml version=': 0, '>': 9, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6920157', 'n_tokens_mistral': 16888, 'n_tokens_neox': 13188, 'n_words': 2423}