kopia lustrzana https://github.com/robhawkes/vizicities
				
				
				
			- Floor is now a circle
- Added option to disable Overpass update on grid move - Added aeroway tags to Overpass - Added ability to load Overpass objects that intersect another way (like an aerodrome boundary) - Stopped initial Overpass requests from blocking main loading0.1
							rodzic
							
								
									a5875435d9
								
							
						
					
					
						commit
						af771e4153
					
				| 
						 | 
				
			
			@ -62,7 +62,11 @@
 | 
			
		|||
		}
 | 
			
		||||
 | 
			
		||||
		_.defaults(options, {
 | 
			
		||||
			coords: [-0.01924, 51.50358]
 | 
			
		||||
			coords: [-0.01924, 51.50358],
 | 
			
		||||
			capZoom: true,
 | 
			
		||||
			capOrbit: true,
 | 
			
		||||
			overpassGridUpdate: true,
 | 
			
		||||
			overpassWayIntersect: false
 | 
			
		||||
		});
 | 
			
		||||
 | 
			
		||||
		// Output city options
 | 
			
		||||
| 
						 | 
				
			
			@ -109,7 +113,9 @@
 | 
			
		|||
			self.publish("loadingProgress", 0.5);
 | 
			
		||||
 | 
			
		||||
			// Set up data loader
 | 
			
		||||
			self.data = new VIZI.DataOverpass();
 | 
			
		||||
			self.data = new VIZI.DataOverpass({
 | 
			
		||||
				gridUpdate: options.overpassGridUpdate
 | 
			
		||||
			});
 | 
			
		||||
 | 
			
		||||
			// TODO: Work out a way to use progress event of each promises to increment loading progress
 | 
			
		||||
			// Perhaps by looping through each promises individually and working out progress fraction by num. of promises / amount processed
 | 
			
		||||
| 
						 | 
				
			
			@ -121,7 +127,7 @@
 | 
			
		|||
			promises.push(self.loadCoreObjects());
 | 
			
		||||
 | 
			
		||||
			// Load data from the OSM Overpass API
 | 
			
		||||
			promises.push(self.loadOverpass());
 | 
			
		||||
			promises.push(self.loadOverpass(options.overpassWayIntersect));
 | 
			
		||||
 | 
			
		||||
			return Q.allSettled(promises);
 | 
			
		||||
		}).then(function (results) {
 | 
			
		||||
| 
						 | 
				
			
			@ -250,18 +256,25 @@
 | 
			
		|||
		return Q.fcall(function() {});
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	VIZI.City.prototype.loadOverpass = function() {
 | 
			
		||||
	VIZI.City.prototype.loadOverpass = function(wayIntersect) {
 | 
			
		||||
		VIZI.Log("Loading data from OSM Overpass API");
 | 
			
		||||
 | 
			
		||||
		var startTime = Date.now();
 | 
			
		||||
 | 
			
		||||
		var deferred = Q.defer();
 | 
			
		||||
		// var deferred = Q.defer();
 | 
			
		||||
 | 
			
		||||
		this.data.update().done(function() {
 | 
			
		||||
			VIZI.Log("Finished loading Overpass data in " + (Date.now() - startTime) + "ms");
 | 
			
		||||
			deferred.resolve();
 | 
			
		||||
		});
 | 
			
		||||
		if (wayIntersect) {
 | 
			
		||||
			this.data.updateByWayIntersect(wayIntersect).done(function() {
 | 
			
		||||
				VIZI.Log("Finished loading Overpass data using way intersection in " + (Date.now() - startTime) + "ms");
 | 
			
		||||
			});
 | 
			
		||||
		} else {
 | 
			
		||||
			this.data.update().done(function() {
 | 
			
		||||
				VIZI.Log("Finished loading Overpass data in " + (Date.now() - startTime) + "ms");
 | 
			
		||||
				// deferred.resolve();
 | 
			
		||||
			});
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return deferred.promise;
 | 
			
		||||
		// return deferred.promise;
 | 
			
		||||
		return Q.fcall(function() {});
 | 
			
		||||
	};
 | 
			
		||||
}());
 | 
			
		||||
| 
						 | 
				
			
			@ -72,7 +72,7 @@
 | 
			
		|||
					return;
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				var features = self.process(data);
 | 
			
		||||
				var features = self.process(data, true);
 | 
			
		||||
 | 
			
		||||
				// Add data to cache (including dupes on boundaries)
 | 
			
		||||
				self.cache.add(cacheKey, features);
 | 
			
		||||
| 
						 | 
				
			
			@ -82,6 +82,12 @@
 | 
			
		|||
 | 
			
		||||
				// Skip duplicate features
 | 
			
		||||
				_.each(features, function(feature) {
 | 
			
		||||
					// Skip if feature is undefined
 | 
			
		||||
					if (!feature) {
 | 
			
		||||
						VIZI.Log("Skipping undefined feature");
 | 
			
		||||
						return;
 | 
			
		||||
					}
 | 
			
		||||
 | 
			
		||||
					if (self.processedIds[feature.id]) {
 | 
			
		||||
						VIZI.Log("Skipping duplicated feature:", feature.id);
 | 
			
		||||
						return;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2,11 +2,15 @@
 | 
			
		|||
(function() {
 | 
			
		||||
	"use strict";
 | 
			
		||||
 | 
			
		||||
	VIZI.DataOverpass = function() {
 | 
			
		||||
	VIZI.DataOverpass = function(options) {
 | 
			
		||||
		VIZI.Log("Inititialising Overpass API manager");
 | 
			
		||||
 | 
			
		||||
		VIZI.Data.call(this);
 | 
			
		||||
 | 
			
		||||
		_.defaults(options, {
 | 
			
		||||
			gridUpdate: true
 | 
			
		||||
		});
 | 
			
		||||
 | 
			
		||||
		// TODO: It's entirely possible that these queries are picking up duplicate ways. Need to look into it.
 | 
			
		||||
		// TODO: Ways that cross over tile boundaries will likely get picked up by a query for each tile. Look into that.
 | 
			
		||||
		// OSM Buildings handles this by not rendering items with an id that it already knows about
 | 
			
		||||
| 
						 | 
				
			
			@ -21,7 +25,7 @@
 | 
			
		|||
			// "rel({s},{w},{n},{e})[landuse~%22grass|meadow|forest%22];" +
 | 
			
		||||
			// ");(._;way(r););(._;node(w););(" +
 | 
			
		||||
			"way({s},{w},{n},{e})[%22building%22];" +
 | 
			
		||||
			"way({s},{w},{n},{e})[aerodrome=%22airport%22];" +
 | 
			
		||||
			"way({s},{w},{n},{e})[aeroway~%22aerodrome|runway%22];" +
 | 
			
		||||
			"way({s},{w},{n},{e})[waterway~%22riverbank|dock%22];" +
 | 
			
		||||
			"way({s},{w},{n},{e})[waterway=%22canal%22][area=%22yes%22];" +
 | 
			
		||||
			"way({s},{w},{n},{e})[natural~%22water|scrub%22];" +
 | 
			
		||||
| 
						 | 
				
			
			@ -51,7 +55,9 @@
 | 
			
		|||
		this.urlHigh = this.urlBase + this.queryHigh;
 | 
			
		||||
		this.urlLow = this.urlBase + this.queryLow;
 | 
			
		||||
 | 
			
		||||
		this.subscribe("gridUpdated", this.update);
 | 
			
		||||
		if (options.gridUpdate) {
 | 
			
		||||
			this.subscribe("gridUpdated", this.update);
 | 
			
		||||
		}
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	VIZI.DataOverpass.prototype = Object.create( VIZI.Data.prototype );
 | 
			
		||||
| 
						 | 
				
			
			@ -124,9 +130,97 @@
 | 
			
		|||
		return deferred.promise;
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	VIZI.DataOverpass.prototype.updateByWayIntersect = function(wayId) {
 | 
			
		||||
		var self = this;
 | 
			
		||||
		var deferred = Q.defer();
 | 
			
		||||
 | 
			
		||||
		var url = this.urlBase + "[out:json];(way(" + wayId + "));(._;node(w););out;";
 | 
			
		||||
 | 
			
		||||
		// Get way to intersect with
 | 
			
		||||
		VIZI.Log("Requesting URL", url);
 | 
			
		||||
 | 
			
		||||
		d3.json(url, function(error, data) {
 | 
			
		||||
			VIZI.Log("Response for URL", url);
 | 
			
		||||
			if (error) {
 | 
			
		||||
				deferred.reject(new Error(error));
 | 
			
		||||
			} else {
 | 
			
		||||
				// No features
 | 
			
		||||
				if (data.elements.length === 0) {
 | 
			
		||||
					deferred.resolve();
 | 
			
		||||
					return;
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				var way = self.process(data, false, false)[0];
 | 
			
		||||
 | 
			
		||||
				VIZI.Log(way);
 | 
			
		||||
				var coordinates = way.coordinates;
 | 
			
		||||
 | 
			
		||||
				var tiles = {};
 | 
			
		||||
 | 
			
		||||
				// Find tiles that intersect way
 | 
			
		||||
				_.each(coordinates, function(coordinate) {
 | 
			
		||||
					var tile = self.grid.lonlat2tile(coordinate[0], coordinate[1], self.geo.tileZoom);
 | 
			
		||||
 | 
			
		||||
					if (!tiles[tile[1]]) {
 | 
			
		||||
						tiles[tile[1]] = [];
 | 
			
		||||
					}
 | 
			
		||||
 | 
			
		||||
					if (_.indexOf(tiles[tile[1]], tile[0]) === -1) {
 | 
			
		||||
						tiles[tile[1]].push(tile[0]);
 | 
			
		||||
					}
 | 
			
		||||
				});
 | 
			
		||||
 | 
			
		||||
				VIZI.Log(tiles);
 | 
			
		||||
 | 
			
		||||
				var promiseQueue = [];
 | 
			
		||||
 | 
			
		||||
				_.each(tiles, function(tilesX, tileY) {
 | 
			
		||||
					// Fill in gaps
 | 
			
		||||
					var minX = _.min(tilesX);
 | 
			
		||||
					var maxX = _.max(tilesX);
 | 
			
		||||
 | 
			
		||||
					var tilesXFilled = _.range(minX, maxX + 1);
 | 
			
		||||
 | 
			
		||||
					_.each(tilesXFilled, function(tileX) {
 | 
			
		||||
						var tileBounds = {
 | 
			
		||||
							n: Number(tileY),
 | 
			
		||||
							e: tileX + 1,
 | 
			
		||||
							s: Number(tileY) + 1,
 | 
			
		||||
							w: tileX
 | 
			
		||||
						};
 | 
			
		||||
						
 | 
			
		||||
						var tileBoundsLonLat = self.grid.getBoundsLonLat(tileBounds);
 | 
			
		||||
 | 
			
		||||
						// VIZI.Log(tileBoundsLonLat);
 | 
			
		||||
 | 
			
		||||
						var cacheKey = tileX + ":" + tileY;
 | 
			
		||||
 | 
			
		||||
						// TODO: Handle load promise without actually running the function
 | 
			
		||||
						// - At the moment, the load function is run in at this point
 | 
			
		||||
						promiseQueue.push([self.load, [self.urlHigh, tileBoundsLonLat, cacheKey]]);
 | 
			
		||||
					});
 | 
			
		||||
				});
 | 
			
		||||
 | 
			
		||||
				// Use throat to limit simultaneous Overpass requests
 | 
			
		||||
				// Without limitation the Overpass API will rate-limit
 | 
			
		||||
				Q.all(promiseQueue.map(throat(1, function(promiseFunc) {
 | 
			
		||||
					return promiseFunc[0].apply(self, promiseFunc[1]);
 | 
			
		||||
				}))).done(function() {
 | 
			
		||||
					// deferred.resolve();
 | 
			
		||||
				}, function(error) {
 | 
			
		||||
					// deferred.reject(error);
 | 
			
		||||
				});
 | 
			
		||||
 | 
			
		||||
				deferred.resolve();
 | 
			
		||||
			}
 | 
			
		||||
		});
 | 
			
		||||
 | 
			
		||||
		return deferred.promise;
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	// Process data (convert from Overpass format to a common ViziCities format)
 | 
			
		||||
	// http://wiki.openstreetmap.org/wiki/Talk:Overpass_API/Language_Guide#JSON_Syntax
 | 
			
		||||
	VIZI.DataOverpass.prototype.process = function(data) {
 | 
			
		||||
	VIZI.DataOverpass.prototype.process = function(data, simple, project) {
 | 
			
		||||
		var self = this;
 | 
			
		||||
 | 
			
		||||
		var nodes = {};
 | 
			
		||||
| 
						 | 
				
			
			@ -137,10 +231,10 @@
 | 
			
		|||
			// Find a way to do this without passing the node and way objects
 | 
			
		||||
			switch (element.type) {
 | 
			
		||||
				case "node":
 | 
			
		||||
					self.processNode(nodes, element);
 | 
			
		||||
					nodes[element.id] = self.processNode(nodes, element, project);
 | 
			
		||||
					break;
 | 
			
		||||
				case "way":
 | 
			
		||||
					self.processWay(nodes, ways, element);
 | 
			
		||||
					ways.push(self.processWay(nodes, element, simple));
 | 
			
		||||
					break;
 | 
			
		||||
			}
 | 
			
		||||
		});
 | 
			
		||||
| 
						 | 
				
			
			@ -148,13 +242,13 @@
 | 
			
		|||
		return ways;
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	VIZI.DataOverpass.prototype.processNode = function(nodes, element) {
 | 
			
		||||
		nodes[element.id] = this.geo.projection([element.lon, element.lat]);
 | 
			
		||||
	VIZI.DataOverpass.prototype.processNode = function(nodes, element, project) {
 | 
			
		||||
		return (project === false) ? [element.lon, element.lat] : this.geo.projection([element.lon, element.lat]);
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	// TODO: Validate polygon to make sure it's renderable (eg. complete, and no cross-overs)
 | 
			
		||||
	// TODO: Use simplify.js to reduce complexity of polygons
 | 
			
		||||
	VIZI.DataOverpass.prototype.processWay = function(nodes, ways, element) {
 | 
			
		||||
	VIZI.DataOverpass.prototype.processWay = function(nodes, element, simple) {
 | 
			
		||||
		var self = this;
 | 
			
		||||
 | 
			
		||||
		var points = element.nodes;
 | 
			
		||||
| 
						 | 
				
			
			@ -183,20 +277,22 @@
 | 
			
		|||
			return;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Simplify coordinates
 | 
			
		||||
		// TODO: Perform this in the worker thread
 | 
			
		||||
		var simplifyTolerance = 1; // Three.js units
 | 
			
		||||
		var simplifiedCoords = simplify(coordinates, simplifyTolerance);
 | 
			
		||||
		if (simple) {
 | 
			
		||||
			// Simplify coordinates
 | 
			
		||||
			// TODO: Perform this in the worker thread
 | 
			
		||||
			var simplifyTolerance = 1; // Three.js units
 | 
			
		||||
			coordinates = simplify(coordinates, simplifyTolerance);
 | 
			
		||||
 | 
			
		||||
		// VIZI.Log("Original coord count:", coordinates.length);
 | 
			
		||||
		// VIZI.Log("Simplified coord count:", simplifiedCoords.length);
 | 
			
		||||
			// VIZI.Log("Original coord count:", coordinates.length);
 | 
			
		||||
			// VIZI.Log("Simplified coord count:", simplifiedCoords.length);
 | 
			
		||||
 | 
			
		||||
		// VIZI.Log("Original coord example:", coordinates[0], coordinates[1]);
 | 
			
		||||
		// VIZI.Log("Simplified coord example:", simplifiedCoords[0], simplifiedCoords[1]);
 | 
			
		||||
			// VIZI.Log("Original coord example:", coordinates[0], coordinates[1]);
 | 
			
		||||
			// VIZI.Log("Simplified coord example:", simplifiedCoords[0], simplifiedCoords[1]);
 | 
			
		||||
 | 
			
		||||
		// Not enough points to make an object
 | 
			
		||||
		if (simplifiedCoords.length < 4) {
 | 
			
		||||
			return;
 | 
			
		||||
			// Not enough points to make an object
 | 
			
		||||
			if (coordinates.length < 4) {
 | 
			
		||||
				return;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		tags.height = this.processHeight(tags);
 | 
			
		||||
| 
						 | 
				
			
			@ -207,11 +303,11 @@
 | 
			
		|||
		// More info: http://gis.stackexchange.com/a/8496/14967
 | 
			
		||||
		// tags.area;
 | 
			
		||||
 | 
			
		||||
		ways.push({
 | 
			
		||||
		return {
 | 
			
		||||
			id: element.id,
 | 
			
		||||
			coordinates: simplifiedCoords,
 | 
			
		||||
			coordinates: coordinates,
 | 
			
		||||
			properties: tags
 | 
			
		||||
		});
 | 
			
		||||
		};
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	VIZI.DataOverpass.prototype.processHeight = function(tags) {
 | 
			
		||||
| 
						 | 
				
			
			@ -230,12 +326,12 @@
 | 
			
		|||
		} else if (tags["building"]) {
 | 
			
		||||
			height = 10 + Math.random() * 10;
 | 
			
		||||
		} else if (tags["landuse"] === "forest") {
 | 
			
		||||
			height = 6;
 | 
			
		||||
			height = 7;
 | 
			
		||||
		// } else if (tags["waterway"] || tags["natural"] && /water|scrub/.test(tags["natural"]) || tags["leisure"] && /park|pitch/.test(tags["leisure"]) || tags["landuse"] && /grass|meadow|commercial|retail|industrial|brownfield/.test(tags["landuse"])) {
 | 
			
		||||
		} else if (tags["waterway"] || tags["natural"] === "water") {
 | 
			
		||||
			height = 4;
 | 
			
		||||
		} else if (tags["natural"] === "scrub" || tags["leisure"] && /park|pitch/.test(tags["leisure"]) || tags["landuse"] && /grass|meadow/.test(tags["landuse"]) || tags["aeroway"] === "runway") {
 | 
			
		||||
			height = 3;
 | 
			
		||||
		} else if (tags["natural"] === "scrub" || tags["leisure"] && /park|pitch/.test(tags["leisure"]) || tags["landuse"] && /grass|meadow/.test(tags["landuse"])) {
 | 
			
		||||
			height = 2;
 | 
			
		||||
		} else {
 | 
			
		||||
			height = 1;
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			@ -259,8 +355,10 @@
 | 
			
		|||
			colour = 0xd8c7b5;
 | 
			
		||||
		} else if (tags["landuse"] && /commercial|retail/.test(tags["landuse"])) {
 | 
			
		||||
			colour = 0xa9bbd6;
 | 
			
		||||
		} else if (tags["aerodrome"] === "airport") {
 | 
			
		||||
		} else if (tags["aeroway"] === "aerodrome") {
 | 
			
		||||
			colour = 0xffffff;
 | 
			
		||||
		} else if (tags["aeroway"] === "runway") {
 | 
			
		||||
			colour = 0x666666;
 | 
			
		||||
		} else {
 | 
			
		||||
			VIZI.Log("Setting default colour for feaure", tags);
 | 
			
		||||
			colour = 0xFF0000;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -23,7 +23,8 @@
 | 
			
		|||
 | 
			
		||||
		// floorContainer.add(floorWire);
 | 
			
		||||
 | 
			
		||||
		var floorGeom = new THREE.PlaneGeometry(40000, 40000, 4, 4);
 | 
			
		||||
		// var floorGeom = new THREE.PlaneGeometry(40000, 40000, 4, 4);
 | 
			
		||||
		var floorGeom = new THREE.CircleGeometry(20000, 32);
 | 
			
		||||
		var floorMat = new THREE.MeshBasicMaterial({color: 0xdddddd});
 | 
			
		||||
		var floor = new THREE.Mesh(floorGeom, floorMat);
 | 
			
		||||
		floor.position.y = -0.4;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Ładowanie…
	
		Reference in New Issue