kopia lustrzana https://github.com/robhawkes/vizicities
First pass at a tile-based loading mechanism
rodzic
15fee9f570
commit
ada0042e71
|
@ -48,7 +48,8 @@ module.exports = function(grunt) {
|
|||
'src/client/objects/Floor.js',
|
||||
'src/client/objects/Building.js',
|
||||
'src/client/data/Data.js',
|
||||
'src/client/data/DataOverpass.js'
|
||||
'src/client/data/DataOverpass.js',
|
||||
'src/client/Grid.js',
|
||||
],
|
||||
dest: 'build/vizi.js'
|
||||
}
|
||||
|
|
|
@ -19,6 +19,9 @@
|
|||
// Geo methods
|
||||
this.geo = undefined;
|
||||
|
||||
// Grid manager
|
||||
this.grid = undefined;
|
||||
|
||||
// Data gathering and processing
|
||||
this.data = undefined;
|
||||
|
||||
|
@ -59,8 +62,8 @@
|
|||
center: options.coords
|
||||
});
|
||||
|
||||
// Set up data loader
|
||||
self.data = new VIZI.DataOverpass();
|
||||
// Set up grid manager
|
||||
self.grid = VIZI.Grid.getInstance();
|
||||
|
||||
// Load city using promises
|
||||
|
||||
|
@ -85,6 +88,14 @@
|
|||
}).then(function() {
|
||||
self.publish("loadingProgress", 0.4);
|
||||
|
||||
// Initialise grid manager
|
||||
return self.initGrid();
|
||||
}).then(function() {
|
||||
self.publish("loadingProgress", 0.5);
|
||||
|
||||
// Set up data loader
|
||||
self.data = new VIZI.DataOverpass();
|
||||
|
||||
// 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
|
||||
|
||||
|
@ -171,6 +182,20 @@
|
|||
return deferred.promise;
|
||||
};
|
||||
|
||||
VIZI.City.prototype.initGrid = function() {
|
||||
var startTime = Date.now();
|
||||
|
||||
var deferred = Q.defer();
|
||||
|
||||
this.grid.init(this.geo.center).then(function(result) {
|
||||
VIZI.Log("Finished intialising grid manager in " + (Date.now() - startTime) + "ms");
|
||||
|
||||
deferred.resolve();
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
};
|
||||
|
||||
VIZI.City.prototype.loadCoreObjects = function() {
|
||||
VIZI.Log("Loading core objects");
|
||||
|
||||
|
|
|
@ -11,15 +11,16 @@
|
|||
this.metersPerLat = 111319.49;
|
||||
|
||||
this.tileSize = 256;
|
||||
this.tileZoom = 14;
|
||||
this.tileZoom = 16;
|
||||
|
||||
this.projection = this.setProjection();
|
||||
this.pixelsPerMeter = this.setPixelsPerMeter();
|
||||
|
||||
// Center of view (different to projection.center())
|
||||
this.center = options.center || [0, 0];
|
||||
this.centerPixels = this.projection(this.center);
|
||||
this.bounds = this.getBounds(this.center);
|
||||
|
||||
this.pixelsPerMeter = this.setPixelsPerMeter();
|
||||
};
|
||||
|
||||
Geo.prototype.setProjection = function() {
|
||||
|
@ -43,6 +44,7 @@
|
|||
// Pixel-per-meter: http://wiki.openstreetmap.org/wiki/Zoom_levels
|
||||
Geo.prototype.setPixelsPerMeter = function() {
|
||||
var pixelsPerLat = this.projection([0, 50])[1] - this.projection([0, 51])[1];
|
||||
// var pixelsPerLat = 6378137 * Math.cos(this.center[1])/Math.pow((this.tileZoom + 8), 2);
|
||||
return pixelsPerLat / this.metersPerLat;
|
||||
};
|
||||
|
||||
|
@ -65,7 +67,7 @@
|
|||
|
||||
Geo.prototype.decimalPlaces = function(num, places) {
|
||||
places = places || 5;
|
||||
return parseFloat(num.toFixed(places));
|
||||
return parseFloat(num).toFixed(places);
|
||||
};
|
||||
|
||||
var instance;
|
||||
|
|
|
@ -0,0 +1,179 @@
|
|||
/* globals window, _, VIZI, Q, THREE */
|
||||
(function() {
|
||||
"use strict";
|
||||
|
||||
VIZI.Grid = (function() {
|
||||
var Grid = function() {
|
||||
VIZI.Log("Inititialising grid manager");
|
||||
|
||||
_.extend(this, VIZI.Mediator);
|
||||
|
||||
// Reference to geo class
|
||||
this.geo = VIZI.Geo.getInstance();
|
||||
|
||||
// Location of grid center
|
||||
this.pos2d = new THREE.Vector2();
|
||||
this.centerTile = [];
|
||||
|
||||
// Tile size and zoom level comes from VIZI.Geo
|
||||
|
||||
// Tiles per direction for high detail
|
||||
this.tilesPerDirectionHigh = 2;
|
||||
|
||||
// Tiles per direction for low detail
|
||||
this.tilesPerDirectionLow = 6;
|
||||
|
||||
// Grid bounds for high detail (in TMS values)
|
||||
this.boundsHigh = {};
|
||||
|
||||
// Grid bounds for high detail (lon, lat)
|
||||
this.boundsHighLonLat = {};
|
||||
|
||||
// Grid bounds for low detail (in TMS values)
|
||||
this.boundsLow = {};
|
||||
|
||||
// Grid bounds for low detail (lon, lat)
|
||||
this.boundsLowLonLat = {};
|
||||
|
||||
// Debug grid model
|
||||
this.gridModel = new THREE.Object3D();
|
||||
};
|
||||
|
||||
Grid.prototype.init = function(coords) {
|
||||
var projCoords = this.geo.projection(coords);
|
||||
|
||||
this.pos2d.x = projCoords[0];
|
||||
this.pos2d.y = projCoords[1];
|
||||
|
||||
this.centerTile = this.lonlat2tile(coords[0], coords[1], this.geo.tileZoom, true);
|
||||
|
||||
this.boundsHigh = this.getBounds(this.tilesPerDirectionHigh);
|
||||
this.boundsLow = this.getBounds(this.tilesPerDirectionLow);
|
||||
|
||||
this.boundsHighLonLat = this.getBoundsLonLat(this.boundsHigh);
|
||||
this.boundsLowLonLat = this.getBoundsLonLat(this.boundsLow);
|
||||
|
||||
// Create debug model for grid
|
||||
var lonLatMin = this.tile2lonlat(Math.floor(this.centerTile[0]), Math.floor(this.centerTile[1]), this.geo.tileZoom);
|
||||
var lonLatMax = this.tile2lonlat(Math.floor(this.centerTile[0])+1, Math.floor(this.centerTile[1])+1, this.geo.tileZoom);
|
||||
|
||||
// Why is this tilesize so random?
|
||||
var tileSize = this.geo.projection(lonLatMax)[0] - this.geo.projection(lonLatMin)[0];
|
||||
|
||||
var tileGeom = new THREE.PlaneGeometry( tileSize, tileSize, 1, 1 );
|
||||
var tileCount = [this.boundsHigh.e-this.boundsHigh.w, this.boundsHigh.s-this.boundsHigh.n];
|
||||
// Rows
|
||||
for (var i = 0; i < tileCount[0]; i++) {
|
||||
// Columns
|
||||
for (var j = 0; j < tileCount[1]; j++) {
|
||||
var tileCoords = [this.boundsHigh.w + j, this.boundsHigh.n + i];
|
||||
|
||||
var tileMat = new THREE.MeshBasicMaterial({color: new THREE.Color(0xFFFFFF * Math.random()), transparent: true, opacity: 0.6});
|
||||
var tile = new THREE.Mesh(tileGeom, tileMat);
|
||||
var position = this.geo.projection(this.tile2lonlat(tileCoords[0], tileCoords[1], this.geo.tileZoom));
|
||||
|
||||
tile.position.y = 10;
|
||||
tile.position.x = position[0] + tileSize / 2;
|
||||
tile.position.z = position[1] + tileSize / 2;
|
||||
tile.rotation.x = - 90 * Math.PI / 180;
|
||||
|
||||
// this.publish("addToScene", tile);
|
||||
this.gridModel.add(tile);
|
||||
}
|
||||
}
|
||||
|
||||
this.publish("addToScene", this.gridModel);
|
||||
|
||||
return Q.fcall(function() {});
|
||||
};
|
||||
|
||||
Grid.prototype.update = function() {
|
||||
// Store old position and bounds
|
||||
var oldPos2d = this.pos2d.clone();
|
||||
|
||||
var oldBoundsHigh = {
|
||||
n: this.boundsHigh.n,
|
||||
e: this.boundsHigh.e,
|
||||
s: this.boundsHigh.s,
|
||||
w: this.boundsHigh.w
|
||||
};
|
||||
|
||||
var oldBoundsLow = {
|
||||
n: this.boundsLow.n,
|
||||
e: this.boundsLow.e,
|
||||
s: this.boundsLow.s,
|
||||
w: this.boundsLow.w
|
||||
};
|
||||
// Update central position
|
||||
// Calculate bounds
|
||||
};
|
||||
|
||||
Grid.prototype.getBounds = function(tileDistance) {
|
||||
var bounds = {
|
||||
n: Math.floor(this.centerTile[1]) - tileDistance,
|
||||
e: Math.ceil(this.centerTile[0]) + tileDistance,
|
||||
s: Math.ceil(this.centerTile[1]) + tileDistance,
|
||||
w: Math.floor(this.centerTile[0]) - tileDistance
|
||||
};
|
||||
|
||||
return bounds;
|
||||
};
|
||||
|
||||
Grid.prototype.getBoundsLonLat = function(bounds) {
|
||||
var max = this.tile2lonlat(bounds.e, bounds.n, this.geo.tileZoom);
|
||||
var min = this.tile2lonlat(bounds.w, bounds.s, this.geo.tileZoom);
|
||||
|
||||
var boundsLonLat = {
|
||||
n: this.geo.decimalPlaces(max[1]),
|
||||
e: this.geo.decimalPlaces(max[0]),
|
||||
s: this.geo.decimalPlaces(min[1]),
|
||||
w: this.geo.decimalPlaces(min[0])
|
||||
};
|
||||
|
||||
return boundsLonLat;
|
||||
};
|
||||
|
||||
Grid.prototype.lonlat2tile = function(lon, lat, zoom, float) {
|
||||
lon = Number(lon);
|
||||
|
||||
var tx, ty;
|
||||
|
||||
if (float) {
|
||||
tx = ((lon+180)/360*Math.pow(2,zoom));
|
||||
ty = ((1-Math.log(Math.tan(lat*Math.PI/180) + 1/Math.cos(lat*Math.PI/180))/Math.PI)/2 *Math.pow(2,zoom));
|
||||
} else {
|
||||
tx = (Math.floor((lon+180)/360*Math.pow(2,zoom)));
|
||||
ty = (Math.floor((1-Math.log(Math.tan(lat*Math.PI/180) + 1/Math.cos(lat*Math.PI/180))/Math.PI)/2 *Math.pow(2,zoom)));
|
||||
}
|
||||
|
||||
return [tx, ty];
|
||||
};
|
||||
|
||||
Grid.prototype.tile2lonlat = function(x, y, z) {
|
||||
var lon = (x/Math.pow(2,z)*360-180);
|
||||
var n = Math.PI-2*Math.PI*y/Math.pow(2,z);
|
||||
var lat = (180/Math.PI*Math.atan(0.5*(Math.exp(n)-Math.exp(-n))));
|
||||
|
||||
return [lon, lat];
|
||||
};
|
||||
|
||||
var instance;
|
||||
|
||||
// an emulation of static variables and methods
|
||||
var _static = {
|
||||
name: "VIZI.Grid",
|
||||
|
||||
// Method for getting an instance. It returns
|
||||
// a singleton instance of a singleton object
|
||||
getInstance: function() {
|
||||
if ( instance === undefined ) {
|
||||
instance = new Grid();
|
||||
}
|
||||
|
||||
return instance;
|
||||
}
|
||||
};
|
||||
|
||||
return _static;
|
||||
}());
|
||||
}());
|
|
@ -8,6 +8,9 @@
|
|||
// Reference to geo class
|
||||
this.geo = VIZI.Geo.getInstance();
|
||||
|
||||
// Reference to grid class
|
||||
this.grid = VIZI.Grid.getInstance();
|
||||
|
||||
// URL of data source
|
||||
this.url = "";
|
||||
|
||||
|
@ -32,7 +35,7 @@
|
|||
// Replace URL placeholders with parameter values
|
||||
var url = this.url.replace(/\{([swne])\}/g, function(value, key) {
|
||||
// Replace with paramter, otherwise keep existing value
|
||||
return parameters[key] || value;
|
||||
return parameters[key];
|
||||
});
|
||||
|
||||
// Request data and fulfil promise
|
||||
|
|
|
@ -7,6 +7,10 @@
|
|||
|
||||
VIZI.Data.call(this);
|
||||
|
||||
// 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
|
||||
// https://github.com/kekscom/osmbuildings/blob/master/src/Data.js#L59
|
||||
this.query = "[out:json];" +
|
||||
"((" +
|
||||
"rel({s},{w},{n},{e})[waterway~%22riverbank|dock%22];" +
|
||||
|
@ -43,7 +47,8 @@
|
|||
|
||||
// TODO: Get bounds of area to retrieve data for
|
||||
// - Likely an event from the geo or controls class as the view is changed
|
||||
var bounds = self.geo.getBounds(self.geo.center, self.dataBoundsDistance);
|
||||
// var bounds = self.geo.getBounds(self.geo.center, self.dataBoundsDistance);
|
||||
var bounds = this.grid.boundsHighLonLat;
|
||||
|
||||
// TODO: Check cache for existing data
|
||||
|
||||
|
@ -155,6 +160,8 @@
|
|||
height = 0.1;
|
||||
}
|
||||
|
||||
height *= this.geo.pixelsPerMeter;
|
||||
|
||||
return height;
|
||||
};
|
||||
|
||||
|
|
Ładowanie…
Reference in New Issue