kopia lustrzana https://github.com/robhawkes/vizicities
283 wiersze
8.4 KiB
JavaScript
283 wiersze
8.4 KiB
JavaScript
/* globals window, _, VIZI */
|
|
|
|
/**
|
|
* Main entry point
|
|
* @author Robin Hawkes - vizicities.com
|
|
*/
|
|
|
|
(function() {
|
|
"use strict";
|
|
|
|
VIZI.World = function(options) {
|
|
if (VIZI.DEBUG) console.log("Initialising VIZI.World");
|
|
|
|
var self = this;
|
|
|
|
self.options = options || {};
|
|
|
|
_.defaults(self.options, {
|
|
crs: VIZI.CRS.EPSG3857,
|
|
center: new VIZI.LatLon(51.50358, -0.01924),
|
|
zoom: 16,
|
|
suppressRenderer: false, // Set true for tests
|
|
layersUI: true,
|
|
picking: false,
|
|
antialias: false
|
|
});
|
|
|
|
if (!self.options.viewport) {
|
|
throw new Error("Required viewport option missing");
|
|
}
|
|
|
|
self.crs = self.options.crs;
|
|
|
|
self.attribution = new VIZI.Attribution({
|
|
element: self.options.viewport
|
|
});
|
|
|
|
// TODO: Store switchboards and layers in an id-referenced object
|
|
self.switchboards = [];
|
|
self.layers = [];
|
|
|
|
// Add UI container
|
|
// TODO: Move into a separate UI class
|
|
var uiContainer = document.createElement("div");
|
|
|
|
// Styling is adding via vizicities.css
|
|
uiContainer.classList.add("vizicities-ui");
|
|
self.options.viewport.appendChild(uiContainer);
|
|
|
|
// Set up layer UI
|
|
if (self.options.layersUI) {
|
|
self.layersUI = new VIZI.LayersUI(self.layers);
|
|
}
|
|
|
|
// TODO: Ability to override this with a scene passed into the options
|
|
// TODO: Pass-through options that tweak scene (antialias, etc)
|
|
self.scene = new VIZI.Scene({
|
|
antialias: self.options.antialias,
|
|
viewport: self.options.viewport,
|
|
// TODO: Remove this when running WebGL tests on Travis is solved
|
|
suppressRenderer: self.options.suppressRenderer,
|
|
picking: self.options.picking
|
|
});
|
|
|
|
self.camera = self.options.camera || new VIZI.Camera({
|
|
aspect: self.options.viewport.clientWidth / self.options.viewport.clientHeight
|
|
});
|
|
|
|
self.camera.addToScene(self.scene);
|
|
|
|
// Origins are used as a fixed base for position projections
|
|
self.origin = new VIZI.LatLon(self.options.center);
|
|
self.originZoom = self.options.zoom;
|
|
|
|
// Zoom and center are a dynamic representation of the current state
|
|
// These don't affect the values returned when projecting positions
|
|
self.zoom = undefined;
|
|
self.center = undefined;
|
|
|
|
self.updateView(self.options.center, self.options.zoom);
|
|
|
|
// Window resize
|
|
window.addEventListener("resize", function(event) {
|
|
self.resizeView(self.options.viewport.clientWidth, self.options.viewport.clientHeight);
|
|
});
|
|
|
|
VIZI.Messenger.on("controls:move", function(point) {
|
|
// TODO: Should be more intelligent about whether this has changed
|
|
var unprojected = self.unproject(point);
|
|
self.updateView(unprojected);
|
|
});
|
|
|
|
VIZI.Messenger.on("controls:zoom", function(distance) {
|
|
// Convert control zoom pixel distance to map zoom
|
|
// TODO: Work out a way to use meters instead of pixels (or not needed?)
|
|
var zoom = Math.ceil(self.crs.altitudeToZoom(distance));
|
|
|
|
// TODO: Should be more intelligent about whether this has changed
|
|
self.zoomTo(zoom);
|
|
});
|
|
};
|
|
|
|
VIZI.World.prototype.project = function(latLon, zoom) {
|
|
var self = this;
|
|
zoom = zoom || self.originZoom;
|
|
|
|
// TODO: Are there ramifications to rounding the pixels?
|
|
var originPoint = self.crs.latLonToPoint(self.origin, zoom, {round: false});
|
|
var projected = self.crs.latLonToPoint(latLon, zoom, {round: false});
|
|
|
|
return projected.clone().subtract(originPoint);
|
|
};
|
|
|
|
VIZI.World.prototype.unproject = function(point, zoom) {
|
|
var self = this;
|
|
zoom = zoom || self.originZoom;
|
|
|
|
// TODO: Are there ramifications to rounding the pixels?
|
|
var originPoint = self.crs.latLonToPoint(self.origin, zoom, {round: false});
|
|
|
|
return self.crs.pointToLatLon(point.clone().add(originPoint), zoom);
|
|
};
|
|
|
|
VIZI.World.prototype.pixelsPerMeter = function(latLon, zoom) {
|
|
var self = this;
|
|
zoom = zoom || self.originZoom;
|
|
|
|
return self.crs.pixelsPerMeter(latLon, zoom);
|
|
};
|
|
|
|
VIZI.World.prototype.addLayer = function(layer) {
|
|
var self = this;
|
|
|
|
self.layers.push(layer);
|
|
self.scene.add(layer.object);
|
|
|
|
// Update layers UI
|
|
if (self.layersUI) {
|
|
self.layersUI.onChange();
|
|
}
|
|
};
|
|
|
|
VIZI.World.prototype.addPickable = function(mesh, id) {
|
|
var self = this;
|
|
|
|
if (!self.options.picking) {
|
|
return;
|
|
}
|
|
|
|
self.scene.addPickable(mesh, id);
|
|
};
|
|
|
|
VIZI.World.prototype.addSwitchboard = function(switchboard) {
|
|
var self = this;
|
|
|
|
self.switchboards.push(switchboard);
|
|
};
|
|
|
|
// Update world and blueprint states on each frame
|
|
VIZI.World.prototype.onTick = function(delta) {
|
|
var self = this;
|
|
|
|
// REMOVED: As new NoFlo approach doesn't use switchboards
|
|
// _.each(self.switchboards, function(switchboard) {
|
|
// switchboard.onTick(delta);
|
|
// });
|
|
|
|
_.each(self.layers, function(layer) {
|
|
layer.onTick(delta);
|
|
});
|
|
};
|
|
|
|
// Render current world state
|
|
VIZI.World.prototype.render = function() {
|
|
var self = this;
|
|
self.scene.render(self.camera);
|
|
};
|
|
|
|
// Centralised method to handle variable changes and firing of events
|
|
// TODO: Trigger events as move and zoom progress
|
|
VIZI.World.prototype.updateView = function(center, zoom) {
|
|
var self = this;
|
|
|
|
if (zoom) {
|
|
self.zoom = zoom;
|
|
}
|
|
|
|
self.center = center;
|
|
|
|
// This will trigger things like a grid update within BlueprintOutput
|
|
VIZI.Messenger.emit("world:updateView", self.center, self.zoom);
|
|
};
|
|
|
|
VIZI.World.prototype.resizeView = function(width, height) {
|
|
var self = this;
|
|
|
|
var aspect = width / height;
|
|
self.camera.changeAspect(aspect);
|
|
|
|
self.scene.resize(width, height);
|
|
};
|
|
|
|
// REMOVED: These don't seem to be of any use in world
|
|
// It makes more sense to change camera movement via the controls
|
|
// These helpers would make more sense if a reference to controls was
|
|
// stored in the world class.
|
|
|
|
// VIZI.World.prototype.moveToLatLon = function(latLon) {
|
|
// var self = this;
|
|
// self.updateView(latLon);
|
|
// };
|
|
|
|
// VIZI.World.prototype.moveToPoint = function(point) {
|
|
// var self = this;
|
|
// // TODO: Are there ramifications to not rounding the pixels?
|
|
// var unprojected = self.unproject(point);
|
|
// self.updateView(unprojected);
|
|
// };
|
|
|
|
// VIZI.World.prototype.moveBy = function(point) {
|
|
// var self = this;
|
|
// // TODO: Are there ramifications to not rounding the pixels?
|
|
// var centerProjected = self.crs.latLonToPoint(self.center, self.zoom);
|
|
// var newPoint = centerProjected.clone().add(point);
|
|
// self.updateView(self.crs.pointToLatLon(newPoint, self.zoom));
|
|
// };
|
|
|
|
// TODO: Should this be handled within the controls?
|
|
// What purpose does it serve here if it's not changing the control zoom?
|
|
VIZI.World.prototype.zoomTo = function(zoom) {
|
|
var self = this;
|
|
self.updateView(self.center, zoom);
|
|
};
|
|
|
|
// TODO: Should this be handled within the controls?
|
|
// What purpose does it serve here if it's not changing the control zoom?
|
|
VIZI.World.prototype.zoomIn = function(delta) {
|
|
var self = this;
|
|
self.updateView(self.center, self.zoom + delta);
|
|
};
|
|
|
|
// TODO: Should this be handled within the controls?
|
|
// What purpose does it serve here if it's not changing the control zoom?
|
|
VIZI.World.prototype.zoomOut = function(delta) {
|
|
var self = this;
|
|
self.updateView(self.center, self.zoom - delta);
|
|
};
|
|
|
|
// TODO: Trigger events as camera change progresses
|
|
// TODO: Should this be handled within the controls?
|
|
// What purpose does it serve here?
|
|
VIZI.World.prototype.lookAtLatLon = function(latLon) {
|
|
var self = this;
|
|
var projected = self.project(latLon);
|
|
self.camera.lookAt(projected);
|
|
};
|
|
|
|
// TODO: Trigger events as camera change progresses
|
|
// TODO: Should this be handled within the controls?
|
|
// What purpose does it serve here?
|
|
VIZI.World.prototype.lookAtPoint = function(point) {
|
|
var self = this;
|
|
|
|
self.camera.lookAt(point);
|
|
};
|
|
|
|
// From: http://stackoverflow.com/a/27412386/997339
|
|
VIZI.World.prototype.worldPositionTo2D = function(position) {
|
|
var self = this;
|
|
|
|
var vector3 = position.clone();
|
|
|
|
// Map to normalized device coordinate (NDC) space
|
|
vector3.project(self.camera.camera);
|
|
|
|
// Map to 2D screen space
|
|
var position2D = new VIZI.Point();
|
|
position2D.x = Math.round((vector3.x + 1) * self.options.viewport.clientWidth / 2),
|
|
position2D.y = Math.round((-vector3.y + 1) * self.options.viewport.clientHeight / 2);
|
|
|
|
return position2D;
|
|
};
|
|
})(); |