Added basic click handlers for GeoJSON features and support picking of lines

master
Robin Hawkes 2016-02-29 23:04:07 +00:00
rodzic e78864f60d
commit 790064f3ac
11 zmienionych plików z 486 dodań i 353 usunięć

218
dist/vizicities.js vendored
Wyświetl plik

@ -11824,6 +11824,10 @@ return /******/ (function(modules) { // webpackBootstrap
options.picking = true;
}
if (this._options.onClick) {
options.onClick = this._options.onClick;
}
return (0, _GeoJSONTile2['default'])(quadcode, this._path, layer, options);
}
@ -11958,6 +11962,7 @@ return /******/ (function(modules) { // webpackBootstrap
picking: false,
topojson: false,
filter: null,
onClick: null,
style: this._defaultStyle
};
@ -12003,6 +12008,9 @@ return /******/ (function(modules) { // webpackBootstrap
// Clear request reference
this._request = null;
// TODO: Properly dispose of picking mesh
this._pickingMesh = null;
_get(Object.getPrototypeOf(GeoJSONTile.prototype), 'destroy', this).call(this);
}
}, {
@ -12217,16 +12225,17 @@ return /******/ (function(modules) { // webpackBootstrap
allFlat: true
};
if (this._options.picking) {
polygons.pickingIds = [];
}
var lines = {
vertices: [],
colours: [],
verticesCount: 0
};
if (this._options.picking) {
polygons.pickingIds = [];
lines.pickingIds = [];
}
var colour = new _three2['default'].Color();
features.forEach(function (feature) {
@ -12267,6 +12276,23 @@ return /******/ (function(modules) { // webpackBootstrap
lines.vertices.push(linestringAttributes.vertices);
lines.colours.push(linestringAttributes.colours);
if (_this3._options.picking) {
var pickingId = _this3._layer.getPickingId();
// Inject picking ID
//
// TODO: Perhaps handle this within the GeoJSON helper
lines.pickingIds.push(pickingId);
if (_this3._options.onClick) {
// TODO: Find a way to properly remove this listener on destroy
_this3._world.on('pick-' + pickingId, function () {
_this3._options.onClick(feature);
});
}
}
lines.verticesCount += linestringAttributes.vertices.length;
}
@ -12291,6 +12317,23 @@ return /******/ (function(modules) { // webpackBootstrap
lines.vertices.push(multiLinestringAttributes.vertices);
lines.colours.push(multiLinestringAttributes.colours);
if (_this3._options.picking) {
var pickingId = _this3._layer.getPickingId();
// Inject picking ID
//
// TODO: Perhaps handle this within the GeoJSON helper
lines.pickingIds.push(pickingId);
if (_this3._options.onClick) {
// TODO: Find a way to properly remove this listener on destroy
_this3._world.on('pick-' + pickingId, function () {
_this3._options.onClick(feature);
});
}
}
lines.verticesCount += multiLinestringAttributes.vertices.length;
}
@ -12331,8 +12374,12 @@ return /******/ (function(modules) { // webpackBootstrap
// TODO: Perhaps handle this within the GeoJSON helper
polygons.pickingIds.push(pickingId);
// TODO: This is probably a good point to listen for picking events
// relating to this feature and do something with them
if (_this3._options.onClick) {
// TODO: Find a way to properly remove this listener on destroy
_this3._world.on('pick-' + pickingId, function () {
_this3._options.onClick(feature);
});
}
}
if (polygons.allFlat && !polygonAttributes.flat) {
@ -12415,6 +12462,17 @@ return /******/ (function(modules) { // webpackBootstrap
// mesh.castShadow = true;
this._mesh.add(mesh);
if (this._options.picking) {
material = new _enginePickingMaterial2['default']();
material.side = _three2['default'].BackSide;
// Make the line wider / easier to pick
material.linewidth = style.lineWidth + material.linePadding;
var pickingMesh = new _three2['default'].LineSegments(geometry, material);
this._pickingMesh.add(pickingMesh);
}
}
// Output polygons
@ -14722,8 +14780,15 @@ return /******/ (function(modules) { // webpackBootstrap
var vertices = new Float32Array(lines.verticesCount * 3);
var colours = new Float32Array(lines.verticesCount * 3);
var pickingIds;
if (lines.pickingIds) {
// One component per vertex (1)
pickingIds = new Float32Array(lines.verticesCount);
}
var _vertices;
var _colour;
var _pickingId;
var lastIndex = 0;
@ -14731,6 +14796,10 @@ return /******/ (function(modules) { // webpackBootstrap
_vertices = lines.vertices[i];
_colour = lines.colours[i];
if (pickingIds) {
_pickingId = lines.pickingIds[i];
}
for (var j = 0; j < _vertices.length; j++) {
var ax = _vertices[j][0] + offset.x;
var ay = _vertices[j][1];
@ -14746,6 +14815,10 @@ return /******/ (function(modules) { // webpackBootstrap
colours[lastIndex * 3 + 1] = c1[1];
colours[lastIndex * 3 + 2] = c1[2];
if (pickingIds) {
pickingIds[lastIndex] = _pickingId;
}
lastIndex++;
}
}
@ -14754,6 +14827,10 @@ return /******/ (function(modules) { // webpackBootstrap
geometry.addAttribute('position', new _three2['default'].BufferAttribute(vertices, 3));
geometry.addAttribute('color', new _three2['default'].BufferAttribute(colours, 3));
if (pickingIds) {
geometry.addAttribute('pickingId', new _three2['default'].BufferAttribute(pickingIds, 1));
}
geometry.computeBoundingBox();
return geometry;
@ -14929,33 +15006,35 @@ return /******/ (function(modules) { // webpackBootstrap
var PickingMaterial = function PickingMaterial() {
_three2['default'].ShaderMaterial.call(this, {
// uniforms: {
// size: {
// type: 'f',
// value: 0.01,
// },
// scale: {
// type: 'f',
// value: 400,
// }
// },
uniforms: {
size: {
type: 'f',
value: 0.01
},
scale: {
type: 'f',
value: 400
}
},
// attributes: ['position', 'id'],
vertexShader: _PickingShader2['default'].vertexShader,
fragmentShader: _PickingShader2['default'].fragmentShader
});
this.linePadding = 2;
};
PickingMaterial.prototype = Object.create(_three2['default'].ShaderMaterial.prototype);
PickingMaterial.prototype.constructor = PickingMaterial;
// PickingMaterial.prototype.setPointSize = function(size) {
// this.uniforms.size.value = size;
// };
//
// PickingMaterial.prototype.setPointScale = function(scale) {
// this.uniforms.scale.value = scale;
// };
PickingMaterial.prototype.setPointSize = function (size) {
this.uniforms.size.value = size;
};
PickingMaterial.prototype.setPointScale = function (scale) {
this.uniforms.scale.value = scale;
};
exports['default'] = PickingMaterial;
module.exports = exports['default'];
@ -15067,6 +15146,10 @@ return /******/ (function(modules) { // webpackBootstrap
var _utilBuffer2 = _interopRequireDefault(_utilBuffer);
var _enginePickingMaterial = __webpack_require__(66);
var _enginePickingMaterial2 = _interopRequireDefault(_enginePickingMaterial);
var GeoJSONLayer = (function (_Layer) {
_inherits(GeoJSONLayer, _Layer);
@ -15080,8 +15163,10 @@ return /******/ (function(modules) { // webpackBootstrap
this._defaultStyle = _utilGeoJSON2['default'].defaultStyle;
var defaults = {
picking: false,
topojson: false,
filter: null,
onClick: null,
style: this._defaultStyle
};
@ -15092,6 +15177,8 @@ return /******/ (function(modules) { // webpackBootstrap
} else {
this._options.style = (0, _lodashAssign2['default'])({}, defaults.style, options.style);
}
this._pickingMesh = new _three2['default'].Object3D();
}
// Initialise without requiring new keyword
@ -15099,6 +15186,8 @@ return /******/ (function(modules) { // webpackBootstrap
_createClass(GeoJSONLayer, [{
key: '_onAdd',
value: function _onAdd(world) {
this.addToPicking(this._pickingMesh);
// Request data from URL if needed
if (typeof this._geojson === 'string') {
this._requestData(this._geojson);
@ -15173,6 +15262,11 @@ return /******/ (function(modules) { // webpackBootstrap
verticesCount: 0
};
if (this._options.picking) {
polygons.pickingIds = [];
lines.pickingIds = [];
}
var colour = new _three2['default'].Color();
features.forEach(function (feature) {
@ -15222,6 +15316,23 @@ return /******/ (function(modules) { // webpackBootstrap
lines.vertices.push(linestringAttributes.vertices);
lines.colours.push(linestringAttributes.colours);
if (_this2._options.picking) {
var pickingId = _this2.getPickingId();
// Inject picking ID
//
// TODO: Perhaps handle this within the GeoJSON helper
lines.pickingIds.push(pickingId);
if (_this2._options.onClick) {
// TODO: Find a way to properly remove this listener on destroy
_this2._world.on('pick-' + pickingId, function () {
_this2._options.onClick(feature);
});
}
}
lines.verticesCount += linestringAttributes.vertices.length;
}
@ -15255,6 +15366,23 @@ return /******/ (function(modules) { // webpackBootstrap
lines.vertices.push(multiLinestringAttributes.vertices);
lines.colours.push(multiLinestringAttributes.colours);
if (_this2._options.picking) {
var pickingId = _this2.getPickingId();
// Inject picking ID
//
// TODO: Perhaps handle this within the GeoJSON helper
lines.pickingIds.push(pickingId);
if (_this2._options.onClick) {
// TODO: Find a way to properly remove this listener on destroy
_this2._world.on('pick-' + pickingId, function () {
_this2._options.onClick(feature);
});
}
}
lines.verticesCount += multiLinestringAttributes.vertices.length;
}
@ -15290,6 +15418,22 @@ return /******/ (function(modules) { // webpackBootstrap
polygons.faces.push(polygonAttributes.faces);
polygons.colours.push(polygonAttributes.colours);
if (_this2._options.picking) {
var pickingId = _this2.getPickingId();
// Inject picking ID
//
// TODO: Perhaps handle this within the GeoJSON helper
polygons.pickingIds.push(pickingId);
if (_this2._options.onClick) {
// TODO: Find a way to properly remove this listener on destroy
_this2._world.on('pick-' + pickingId, function () {
_this2._options.onClick(feature);
});
}
}
if (polygons.allFlat && !polygonAttributes.flat) {
polygons.allFlat = false;
}
@ -15302,6 +15446,12 @@ return /******/ (function(modules) { // webpackBootstrap
var material;
var mesh;
if (this._options.picking) {
// Move picking mesh to origin Point
this._pickingMesh.position.x = -offset.x;
this._pickingMesh.position.z = -offset.y;
}
// Output lines
if (lines.vertices.length > 0) {
geometry = _utilBuffer2['default'].createLineGeometry(lines, offset);
@ -15325,6 +15475,17 @@ return /******/ (function(modules) { // webpackBootstrap
// mesh.castShadow = true;
this.add(mesh);
if (this._options.picking) {
material = new _enginePickingMaterial2['default']();
material.side = _three2['default'].BackSide;
// Make the line wider / easier to pick
material.linewidth = style.lineWidth + material.linePadding;
var pickingMesh = new _three2['default'].LineSegments(geometry, material);
this._pickingMesh.add(pickingMesh);
}
}
// Output polygons
@ -15358,6 +15519,14 @@ return /******/ (function(modules) { // webpackBootstrap
}
this.add(mesh);
if (this._options.picking) {
material = new _enginePickingMaterial2['default']();
material.side = _three2['default'].BackSide;
var pickingMesh = new _three2['default'].Mesh(geometry, material);
this._pickingMesh.add(pickingMesh);
}
}
// Move layer to origin Point
@ -15387,6 +15556,9 @@ return /******/ (function(modules) { // webpackBootstrap
// Clear request reference
this._request = null;
// TODO: Properly dispose of picking mesh
this._pickingMesh = null;
// Run common destruction logic from parent
_get(Object.getPrototypeOf(GeoJSONLayer.prototype), 'destroy', this).call(this);
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Wyświetl plik

@ -13,7 +13,7 @@ var imageTileLayer = VIZI.ImageTileLayer('http://{s}.basemaps.cartocdn.com/light
// Building and roads from Mapzen (polygons and linestrings)
var topoJSONTileLayer = VIZI.TopoJSONTileLayer('https://vector.mapzen.com/osm/buildings,roads/{z}/{x}/{y}.topojson?api_key=vector-tiles-NT5Emiw', {
picking: true,
// picking: true,
style: function(feature) {
var height;
@ -33,10 +33,13 @@ var topoJSONTileLayer = VIZI.TopoJSONTileLayer('https://vector.mapzen.com/osm/bu
lineRenderOrder: 2
};
},
// onClick: function(feature) {
// console.log('Clicked:', feature);
// },
attribution: '&copy; <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors, <a href="http://whosonfirst.mapzen.com#License">Who\'s On First</a>.'
}).addTo(world);
// // Just buildings from Mapzen (polygons)
// Just buildings from Mapzen (polygons)
// var topoJSONTileLayer = VIZI.TopoJSONTileLayer('https://vector.mapzen.com/osm/buildings/{z}/{x}/{y}.topojson?api_key=vector-tiles-NT5Emiw', {
// style: function(feature) {
// var height;
@ -61,8 +64,9 @@ var topoJSONTileLayer = VIZI.TopoJSONTileLayer('https://vector.mapzen.com/osm/bu
// attribution: '&copy; <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors, <a href="http://whosonfirst.mapzen.com#License">Who\'s On First</a>.'
// }).addTo(world);
// // London Underground lines
// London Underground lines
// var geoJSONLayer = VIZI.GeoJSONLayer('https://rawgit.com/robhawkes/4acb9d6a6a5f00a377e2/raw/30ae704a44e10f2e13fb7e956e80c3b22e8e7e81/tfl_lines.json', {
// picking: true,
// style: {
// lineColor: '#f7c616',
// // lineHeight: 20,
@ -72,5 +76,8 @@ var topoJSONTileLayer = VIZI.TopoJSONTileLayer('https://vector.mapzen.com/osm/bu
// lineBlending: THREE.AdditiveBlending,
// lineRenderOrder: 2
// },
// onClick: function(feature) {
// console.log('Clicked:', feature);
// },
// attribution: '&copy; Transport for London.'
// }).addTo(world);

Wyświetl plik

@ -1,297 +0,0 @@
import EventEmitter from 'eventemitter3';
import extend from 'lodash.assign';
import CRS from './geo/crs/index';
import Point from './geo/Point';
import LatLon from './geo/LatLon';
import Engine from './engine/Engine';
// TODO: Make sure nothing is left behind in the heap after calling destroy()
// Pretty much any event someone using ViziCities would need will be emitted or
// proxied by World (eg. render events, etc)
class World extends EventEmitter {
constructor(domId, options) {
super();
var defaults = {
crs: CRS.EPSG3857,
skybox: false
};
this.options = extend({}, defaults, options);
this._layers = [];
this._controls = [];
this._initContainer(domId);
this._initEngine();
this._initEnvironment();
this._initEvents();
this._pause = false;
// Kick off the update and render loop
this._update();
}
_initContainer(domId) {
this._container = document.getElementById(domId);
}
_initEngine() {
this._engine = Engine(this._container, this);
// Engine events
//
// Consider proxying these through events on World for public access
// this._engine.on('preRender', () => {});
// this._engine.on('postRender', () => {});
}
_initEnvironment() {
// Not sure if I want to keep this as a private API
//
// Makes sense to allow others to customise their environment so perhaps
// add some method of disable / overriding the environment settings
this._environment = VIZI.EnvironmentLayer({
skybox: this.options.skybox
}).addTo(this);
}
_initEvents() {
this.on('controlsMoveEnd', this._onControlsMoveEnd);
}
_onControlsMoveEnd(point) {
var _point = Point(point.x, point.z);
this._resetView(this.pointToLatLon(_point), _point);
}
// Reset world view
_resetView(latlon, point) {
this.emit('preResetView');
this._moveStart();
this._move(latlon, point);
this._moveEnd();
this.emit('postResetView');
}
_moveStart() {
this.emit('moveStart');
}
_move(latlon, point) {
this._lastPosition = latlon;
this.emit('move', latlon, point);
}
_moveEnd() {
this.emit('moveEnd');
}
_update() {
if (this._pause) {
return;
}
var delta = this._engine.clock.getDelta();
// Once _update is called it will run forever, for now
window.requestAnimationFrame(this._update.bind(this));
// Update controls
this._controls.forEach(controls => {
controls.update();
});
this.emit('preUpdate', delta);
this._engine.update(delta);
this.emit('postUpdate', delta);
}
// Set world view
setView(latlon) {
// Store initial geographic coordinate for the [0,0,0] world position
//
// The origin point doesn't move in three.js / 3D space so only set it once
// here instead of every time _resetView is called
//
// If it was updated every time then coorindates would shift over time and
// would be out of place / context with previously-placed points (0,0 would
// refer to a different point each time)
this._originLatlon = latlon;
this._originPoint = this.project(latlon);
this._resetView(latlon);
return this;
}
// Return world geographic position
getPosition() {
return this._lastPosition;
}
// Transform geographic coordinate to world point
//
// This doesn't take into account the origin offset
//
// For example, this takes a geographic coordinate and returns a point
// relative to the origin point of the projection (not the world)
project(latlon) {
return this.options.crs.latLonToPoint(LatLon(latlon));
}
// Transform world point to geographic coordinate
//
// This doesn't take into account the origin offset
//
// For example, this takes a point relative to the origin point of the
// projection (not the world) and returns a geographic coordinate
unproject(point) {
return this.options.crs.pointToLatLon(Point(point));
}
// Takes into account the origin offset
//
// For example, this takes a geographic coordinate and returns a point
// relative to the three.js / 3D origin (0,0)
latLonToPoint(latlon) {
var projectedPoint = this.project(LatLon(latlon));
return projectedPoint._subtract(this._originPoint);
}
// Takes into account the origin offset
//
// For example, this takes a point relative to the three.js / 3D origin (0,0)
// and returns the exact geographic coordinate at that point
pointToLatLon(point) {
var projectedPoint = Point(point).add(this._originPoint);
return this.unproject(projectedPoint);
}
// Return pointscale for a given geographic coordinate
pointScale(latlon, accurate) {
return this.options.crs.pointScale(latlon, accurate);
}
// Convert from real meters to world units
//
// TODO: Would be nice not to have to pass in a pointscale here
metresToWorld(metres, pointScale, zoom) {
return this.options.crs.metresToWorld(metres, pointScale, zoom);
}
// Convert from real meters to world units
//
// TODO: Would be nice not to have to pass in a pointscale here
worldToMetres(worldUnits, pointScale, zoom) {
return this.options.crs.worldToMetres(worldUnits, pointScale, zoom);
}
// Unsure if it's a good idea to expose this here for components like
// GridLayer to use (eg. to keep track of a frustum)
getCamera() {
return this._engine._camera;
}
addLayer(layer) {
layer._addToWorld(this);
this._layers.push(layer);
// Could move this into Layer but it'll do here for now
this._engine._scene.add(layer._layer);
this.emit('layerAdded', layer);
return this;
}
// Remove layer from world and scene but don't destroy it entirely
removeLayer(layer) {
var layerIndex = this._layers.indexOf(layer);
if (layerIndex > -1) {
// Remove from this._layers
this._layers.splice(layerIndex, 1);
};
this._engine._scene.remove(layer._layer);
this.emit('layerRemoved');
return this;
}
addControls(controls) {
controls._addToWorld(this);
this._controls.push(controls);
this.emit('controlsAdded', controls);
return this;
}
// Remove controls from world but don't destroy them entirely
removeControls(controls) {
var controlsIndex = this._controls.indexOf(controlsIndex);
if (controlsIndex > -1) {
this._controls.splice(controlsIndex, 1);
};
this.emit('controlsRemoved', controls);
return this;
}
stop() {
this._pause = true;
}
start() {
this._pause = false;
this._update();
}
// Destroys the world(!) and removes it from the scene and memory
//
// TODO: World out why so much three.js stuff is left in the heap after this
destroy() {
this.stop();
// Remove listeners
this.off('controlsMoveEnd', this._onControlsMoveEnd);
var i;
// Remove all controls
var controls;
for (i = this._controls.length - 1; i >= 0; i--) {
controls = this._controls[0];
this.removeControls(controls);
controls.destroy();
};
// Remove all layers
var layer;
for (i = this._layers.length - 1; i >= 0; i--) {
layer = this._layers[0];
this.removeLayer(layer);
layer.destroy();
};
// Environment layer is removed with the other layers
this._environment = null;
this._engine.destroy();
this._engine = null;
// TODO: Probably should clean the container too / remove the canvas
this._container = null;
}
}
// Initialise without requiring new keyword
export default function(domId, options) {
return new World(domId, options);
};

Wyświetl plik

@ -0,0 +1,82 @@
import EventEmitter from 'eventemitter3';
import THREE from 'three';
import Scene from './Scene';
import Renderer from './Renderer';
import Camera from './Camera';
import Picking from './Picking';
class Engine extends EventEmitter {
constructor(container, world) {
console.log('Init Engine');
super();
this._world = world;
this._scene = Scene;
this._renderer = Renderer(container);
this._camera = Camera(container);
// TODO: Make this optional
this._picking = Picking(this._world, this._renderer, this._camera);
this.clock = new THREE.Clock();
this._frustum = new THREE.Frustum();
}
update(delta) {
this.emit('preRender');
this._renderer.render(this._scene, this._camera);
// Render picking scene
// this._renderer.render(this._picking._pickingScene, this._camera);
this.emit('postRender');
}
destroy() {
// Remove any remaining objects from scene
var child;
for (i = this._scene.children.length - 1; i >= 0; i--) {
child = this._scene.children[i];
if (!child) {
continue;
}
this._scene.remove(child);
if (child.geometry) {
// Dispose of mesh and materials
child.geometry.dispose();
child.geometry = null;
}
if (child.material) {
if (child.material.map) {
child.material.map.dispose();
child.material.map = null;
}
child.material.dispose();
child.material = null;
}
};
this._picking.destroy();
this._picking = null;
this._world = null;
this._scene = null;
this._renderer = null;
this._camera = null;
this._clock = null;
this._frustum = null;
}
}
// Initialise without requiring new keyword
export default function(container, world) {
return new Engine(container, world);
};

Wyświetl plik

@ -5,32 +5,34 @@ import PickingShader from './PickingShader';
var PickingMaterial = function() {
THREE.ShaderMaterial.call(this, {
// uniforms: {
// size: {
// type: 'f',
// value: 0.01,
// },
// scale: {
// type: 'f',
// value: 400,
// }
// },
uniforms: {
size: {
type: 'f',
value: 0.01,
},
scale: {
type: 'f',
value: 400,
}
},
// attributes: ['position', 'id'],
vertexShader: PickingShader.vertexShader,
fragmentShader: PickingShader.fragmentShader
});
this.linePadding = 2;
};
PickingMaterial.prototype = Object.create(THREE.ShaderMaterial.prototype);
PickingMaterial.prototype.constructor = PickingMaterial;
// PickingMaterial.prototype.setPointSize = function(size) {
// this.uniforms.size.value = size;
// };
//
// PickingMaterial.prototype.setPointScale = function(scale) {
// this.uniforms.scale.value = scale;
// };
PickingMaterial.prototype.setPointSize = function(size) {
this.uniforms.size.value = size;
};
PickingMaterial.prototype.setPointScale = function(scale) {
this.uniforms.scale.value = scale;
};
export default PickingMaterial;

Wyświetl plik

@ -6,6 +6,7 @@ import Point from '../geo/Point';
import LatLon from '../geo/LatLon';
import GeoJSON from '../util/GeoJSON';
import Buffer from '../util/Buffer';
import PickingMaterial from '../engine/PickingMaterial';
class GeoJSONLayer extends Layer {
constructor(geojson, options) {
@ -16,8 +17,10 @@ class GeoJSONLayer extends Layer {
this._defaultStyle = GeoJSON.defaultStyle;
var defaults = {
picking: false,
topojson: false,
filter: null,
onClick: null,
style: this._defaultStyle
};
@ -28,9 +31,13 @@ class GeoJSONLayer extends Layer {
} else {
this._options.style = extend({}, defaults.style, options.style);
}
this._pickingMesh = new THREE.Object3D();
}
_onAdd(world) {
this.addToPicking(this._pickingMesh);
// Request data from URL if needed
if (typeof this._geojson === 'string') {
this._requestData(this._geojson);
@ -99,6 +106,11 @@ class GeoJSONLayer extends Layer {
verticesCount: 0
};
if (this._options.picking) {
polygons.pickingIds = [];
lines.pickingIds = [];
}
var colour = new THREE.Color();
features.forEach(feature => {
@ -152,6 +164,23 @@ class GeoJSONLayer extends Layer {
lines.vertices.push(linestringAttributes.vertices);
lines.colours.push(linestringAttributes.colours);
if (this._options.picking) {
var pickingId = this.getPickingId();
// Inject picking ID
//
// TODO: Perhaps handle this within the GeoJSON helper
lines.pickingIds.push(pickingId);
if (this._options.onClick) {
// TODO: Find a way to properly remove this listener on destroy
this._world.on('pick-' + pickingId, () => {
this._options.onClick(feature);
});
}
}
lines.verticesCount += linestringAttributes.vertices.length;
}
@ -185,6 +214,23 @@ class GeoJSONLayer extends Layer {
lines.vertices.push(multiLinestringAttributes.vertices);
lines.colours.push(multiLinestringAttributes.colours);
if (this._options.picking) {
var pickingId = this.getPickingId();
// Inject picking ID
//
// TODO: Perhaps handle this within the GeoJSON helper
lines.pickingIds.push(pickingId);
if (this._options.onClick) {
// TODO: Find a way to properly remove this listener on destroy
this._world.on('pick-' + pickingId, () => {
this._options.onClick(feature);
});
}
}
lines.verticesCount += multiLinestringAttributes.vertices.length;
}
@ -220,6 +266,22 @@ class GeoJSONLayer extends Layer {
polygons.faces.push(polygonAttributes.faces);
polygons.colours.push(polygonAttributes.colours);
if (this._options.picking) {
var pickingId = this.getPickingId();
// Inject picking ID
//
// TODO: Perhaps handle this within the GeoJSON helper
polygons.pickingIds.push(pickingId);
if (this._options.onClick) {
// TODO: Find a way to properly remove this listener on destroy
this._world.on('pick-' + pickingId, () => {
this._options.onClick(feature);
});
}
}
if (polygons.allFlat && !polygonAttributes.flat) {
polygons.allFlat = false;
}
@ -232,6 +294,12 @@ class GeoJSONLayer extends Layer {
var material;
var mesh;
if (this._options.picking) {
// Move picking mesh to origin Point
this._pickingMesh.position.x = -offset.x;
this._pickingMesh.position.z = -offset.y;
}
// Output lines
if (lines.vertices.length > 0) {
geometry = Buffer.createLineGeometry(lines, offset);
@ -255,6 +323,17 @@ class GeoJSONLayer extends Layer {
// mesh.castShadow = true;
this.add(mesh);
if (this._options.picking) {
material = new PickingMaterial();
material.side = THREE.BackSide;
// Make the line wider / easier to pick
material.linewidth = style.lineWidth + material.linePadding;
var pickingMesh = new THREE.LineSegments(geometry, material);
this._pickingMesh.add(pickingMesh);
}
}
// Output polygons
@ -288,6 +367,14 @@ class GeoJSONLayer extends Layer {
}
this.add(mesh);
if (this._options.picking) {
material = new PickingMaterial();
material.side = THREE.BackSide;
var pickingMesh = new THREE.Mesh(geometry, material);
this._pickingMesh.add(pickingMesh);
}
}
// Move layer to origin Point
@ -315,6 +402,9 @@ class GeoJSONLayer extends Layer {
// Clear request reference
this._request = null;
// TODO: Properly dispose of picking mesh
this._pickingMesh = null;
// Run common destruction logic from parent
super.destroy();
}

Wyświetl plik

@ -53,6 +53,7 @@ class GeoJSONTile extends Tile {
picking: false,
topojson: false,
filter: null,
onClick: null,
style: this._defaultStyle
};
@ -90,6 +91,9 @@ class GeoJSONTile extends Tile {
// Clear request reference
this._request = null;
// TODO: Properly dispose of picking mesh
this._pickingMesh = null;
super.destroy();
}
@ -293,16 +297,17 @@ class GeoJSONTile extends Tile {
allFlat: true
};
if (this._options.picking) {
polygons.pickingIds = [];
}
var lines = {
vertices: [],
colours: [],
verticesCount: 0
};
if (this._options.picking) {
polygons.pickingIds = [];
lines.pickingIds = [];
}
var colour = new THREE.Color();
features.forEach(feature => {
@ -347,6 +352,23 @@ class GeoJSONTile extends Tile {
lines.vertices.push(linestringAttributes.vertices);
lines.colours.push(linestringAttributes.colours);
if (this._options.picking) {
var pickingId = this._layer.getPickingId();
// Inject picking ID
//
// TODO: Perhaps handle this within the GeoJSON helper
lines.pickingIds.push(pickingId);
if (this._options.onClick) {
// TODO: Find a way to properly remove this listener on destroy
this._world.on('pick-' + pickingId, () => {
this._options.onClick(feature);
});
}
}
lines.verticesCount += linestringAttributes.vertices.length;
}
@ -371,6 +393,23 @@ class GeoJSONTile extends Tile {
lines.vertices.push(multiLinestringAttributes.vertices);
lines.colours.push(multiLinestringAttributes.colours);
if (this._options.picking) {
var pickingId = this._layer.getPickingId();
// Inject picking ID
//
// TODO: Perhaps handle this within the GeoJSON helper
lines.pickingIds.push(pickingId);
if (this._options.onClick) {
// TODO: Find a way to properly remove this listener on destroy
this._world.on('pick-' + pickingId, () => {
this._options.onClick(feature);
});
}
}
lines.verticesCount += multiLinestringAttributes.vertices.length;
}
@ -411,8 +450,12 @@ class GeoJSONTile extends Tile {
// TODO: Perhaps handle this within the GeoJSON helper
polygons.pickingIds.push(pickingId);
// TODO: This is probably a good point to listen for picking events
// relating to this feature and do something with them
if (this._options.onClick) {
// TODO: Find a way to properly remove this listener on destroy
this._world.on('pick-' + pickingId, () => {
this._options.onClick(feature);
});
}
}
if (polygons.allFlat && !polygonAttributes.flat) {
@ -495,6 +538,17 @@ class GeoJSONTile extends Tile {
// mesh.castShadow = true;
this._mesh.add(mesh);
if (this._options.picking) {
material = new PickingMaterial();
material.side = THREE.BackSide;
// Make the line wider / easier to pick
material.linewidth = style.lineWidth + material.linePadding;
var pickingMesh = new THREE.LineSegments(geometry, material);
this._pickingMesh.add(pickingMesh);
}
}
// Output polygons

Wyświetl plik

@ -111,6 +111,10 @@ class GeoJSONTileLayer extends TileLayer {
options.picking = true;
}
if (this._options.onClick) {
options.onClick = this._options.onClick;
}
return GeoJSONTile(quadcode, this._path, layer, options);
}

Wyświetl plik

@ -11,8 +11,15 @@ var Buffer = (function() {
var vertices = new Float32Array(lines.verticesCount * 3);
var colours = new Float32Array(lines.verticesCount * 3);
var pickingIds;
if (lines.pickingIds) {
// One component per vertex (1)
pickingIds = new Float32Array(lines.verticesCount);
}
var _vertices;
var _colour;
var _pickingId;
var lastIndex = 0;
@ -20,6 +27,10 @@ var Buffer = (function() {
_vertices = lines.vertices[i];
_colour = lines.colours[i];
if (pickingIds) {
_pickingId = lines.pickingIds[i];
}
for (var j = 0; j < _vertices.length; j++) {
var ax = _vertices[j][0] + offset.x;
var ay = _vertices[j][1];
@ -35,6 +46,10 @@ var Buffer = (function() {
colours[lastIndex * 3 + 1] = c1[1];
colours[lastIndex * 3 + 2] = c1[2];
if (pickingIds) {
pickingIds[lastIndex] = _pickingId;
}
lastIndex++;
}
}
@ -43,6 +58,10 @@ var Buffer = (function() {
geometry.addAttribute('position', new THREE.BufferAttribute(vertices, 3));
geometry.addAttribute('color', new THREE.BufferAttribute(colours, 3));
if (pickingIds) {
geometry.addAttribute('pickingId', new THREE.BufferAttribute(pickingIds, 1));
}
geometry.computeBoundingBox();
return geometry;