First pass at a tile-based loading mechanism

0.1
Robin Hawkes 2014-02-11 23:18:39 +00:00
rodzic 15fee9f570
commit ada0042e71
6 zmienionych plików z 225 dodań i 8 usunięć

Wyświetl plik

@ -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'
}

Wyświetl plik

@ -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");

Wyświetl plik

@ -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;

179
src/client/Grid.js 100644
Wyświetl plik

@ -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;
}());
}());

Wyświetl plik

@ -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

Wyświetl plik

@ -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;
};