kopia lustrzana https://github.com/robhawkes/vizicities
Points can now be generated using Web Workers
rodzic
e4d2db0388
commit
a0a2c93f45
|
@ -7,6 +7,7 @@ import Buffer from '../util/Buffer';
|
|||
import Stringify from '../util/Stringify';
|
||||
import PolygonLayer from './geometry/PolygonLayer';
|
||||
import PolylineLayer from './geometry/PolylineLayer';
|
||||
import PointLayer from './geometry/PointLayer';
|
||||
import {latLon as LatLon} from '../geo/LatLon';
|
||||
import {point as Point} from '../geo/Point';
|
||||
import Geo from '../geo/Geo';
|
||||
|
@ -106,6 +107,10 @@ class GeoJSONWorkerLayer extends Layer {
|
|||
this._processPolylineResults(results.polylines);
|
||||
}
|
||||
|
||||
if (results.points) {
|
||||
this._processPointResults(results.points);
|
||||
}
|
||||
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
|
@ -322,6 +327,112 @@ class GeoJSONWorkerLayer extends Layer {
|
|||
}
|
||||
}
|
||||
|
||||
_processPointResults(results) {
|
||||
var splitPositions = Buffer.splitFloat32Array(results.attributes.positions);
|
||||
var splitNormals = Buffer.splitFloat32Array(results.attributes.normals);
|
||||
var splitColors = Buffer.splitFloat32Array(results.attributes.colors);
|
||||
|
||||
var splitProperties;
|
||||
if (results.properties) {
|
||||
splitProperties = Buffer.splitUint8Array(results.properties);
|
||||
}
|
||||
|
||||
var flats = results.flats;
|
||||
|
||||
var objects = [];
|
||||
var obj;
|
||||
var pickingId;
|
||||
var pickingIds;
|
||||
var properties;
|
||||
|
||||
var pointAttributeLengths = {
|
||||
positions: 3,
|
||||
normals: 3,
|
||||
colors: 3
|
||||
};
|
||||
|
||||
for (var i = 0; i < splitPositions.length; i++) {
|
||||
if (splitProperties && splitProperties[i]) {
|
||||
properties = JSON.parse(Buffer.uint8ArrayToString(splitProperties[i]));
|
||||
} else {
|
||||
properties = {};
|
||||
}
|
||||
|
||||
// WORKERS: obj.attributes should actually an array of polygons for
|
||||
// the feature, though the current logic isn't aware of that
|
||||
obj = {
|
||||
attributes: [{
|
||||
positions: splitPositions[i],
|
||||
normals: splitNormals[i],
|
||||
colors: splitColors[i]
|
||||
}],
|
||||
properties: properties,
|
||||
flat: flats[i]
|
||||
};
|
||||
|
||||
// WORKERS: If interactive, generate unique ID for each feature, create
|
||||
// the buffer attributes and set up event listeners
|
||||
if (this._options.interactive) {
|
||||
pickingId = this.getPickingId();
|
||||
|
||||
pickingIds = new Float32Array(splitPositions[i].length / 3);
|
||||
pickingIds.fill(pickingId);
|
||||
|
||||
obj.attributes[0].pickingIds = pickingIds;
|
||||
|
||||
pointAttributeLengths.pickingIds = 1;
|
||||
|
||||
this._addPicking(pickingId, properties);
|
||||
}
|
||||
|
||||
// TODO: Make this specific to polygon attributes
|
||||
if (typeof this._options.onAddAttributes === 'function') {
|
||||
var customAttributes = this._options.onAddAttributes(obj.attributes[0], properties);
|
||||
var customAttribute;
|
||||
for (var key in customAttributes) {
|
||||
customAttribute = customAttributes[key];
|
||||
obj.attributes[0][key] = customAttribute.value;
|
||||
pointAttributeLengths[key] = customAttribute.length;
|
||||
}
|
||||
}
|
||||
|
||||
objects.push(obj);
|
||||
}
|
||||
|
||||
var pointAttributes = [];
|
||||
|
||||
var pointFlat = true;
|
||||
|
||||
var obj;
|
||||
for (var i = 0; i < objects.length; i++) {
|
||||
obj = objects[i];
|
||||
|
||||
if (pointFlat && !obj.flat) {
|
||||
pointFlat = false;
|
||||
}
|
||||
|
||||
var bufferAttributes = Buffer.mergeAttributes(obj.attributes);
|
||||
pointAttributes.push(bufferAttributes);
|
||||
};
|
||||
|
||||
if (pointAttributes.length > 0) {
|
||||
var mergedPointAttributes = Buffer.mergeAttributes(pointAttributes);
|
||||
|
||||
// TODO: Make this work when style is a function per feature
|
||||
var style = (typeof this._options.style === 'function') ? this._options.style(objects[0]) : this._options.style;
|
||||
style = extend({}, GeoJSON.defaultStyle, style);
|
||||
|
||||
this._setPointMesh(mergedPointAttributes, pointAttributeLengths, style, pointFlat).then((result) => {
|
||||
this._pointMesh = result.mesh;
|
||||
this.add(this._pointMesh);
|
||||
|
||||
if (result.pickingMesh) {
|
||||
this._pickingMesh.add(result.pickingMesh);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: At some point this needs to return all the features to the main thread
|
||||
// so it can generate meshes and output to the scene, as well as perhaps creating
|
||||
// individual layers / components for each feature to track things like picking
|
||||
|
@ -329,6 +440,8 @@ class GeoJSONWorkerLayer extends Layer {
|
|||
//
|
||||
// TODO: Find a way so the origin point isn't needed to be passed in as it
|
||||
// feels a bit messy and against the idea of a static Geo class
|
||||
//
|
||||
// TODO: Support passing custom geometry for point layers
|
||||
static Process(geojson, topojson, headers, originPoint, _style, _properties) {
|
||||
return new Promise((resolve, reject) => {
|
||||
GeoJSONWorkerLayer.ProcessGeoJSON(geojson, headers).then((res) => {
|
||||
|
@ -346,6 +459,7 @@ class GeoJSONWorkerLayer extends Layer {
|
|||
var pointScale;
|
||||
var polygons = [];
|
||||
var polylines = [];
|
||||
var points = [];
|
||||
|
||||
// Deserialise style function if provided
|
||||
if (typeof _style === 'string') {
|
||||
|
@ -450,11 +564,43 @@ class GeoJSONWorkerLayer extends Layer {
|
|||
polylines.push(polyline);
|
||||
}
|
||||
|
||||
if (geometry.type === 'Point' || geometry.type === 'MultiPoint') {}
|
||||
if (geometry.type === 'Point' || geometry.type === 'MultiPoint') {
|
||||
coordinates = (PointLayer.isSingle(coordinates)) ? [coordinates] : coordinates;
|
||||
|
||||
var converted = coordinates.map(coordinate => {
|
||||
return LatLon(coordinate[1], coordinate[0]);
|
||||
});
|
||||
|
||||
var point;
|
||||
var projected = converted.map((latlon) => {
|
||||
point = Geo.latLonToPoint(latlon)._subtract(originPoint);
|
||||
|
||||
if (!pointScale) {
|
||||
pointScale = Geo.pointScale(latlon);
|
||||
}
|
||||
|
||||
return point;
|
||||
});
|
||||
|
||||
var point = {
|
||||
projected: projected,
|
||||
options: {
|
||||
pointScale: pointScale,
|
||||
style: style
|
||||
}
|
||||
};
|
||||
|
||||
if (_properties) {
|
||||
point.properties = feature.properties;
|
||||
}
|
||||
|
||||
points.push(point);
|
||||
}
|
||||
};
|
||||
|
||||
var polygonBufferPromises = [];
|
||||
var polylineBufferPromises = [];
|
||||
var pointBufferPromises = [];
|
||||
|
||||
var polygon;
|
||||
for (var i = 0; i < polygons.length; i++) {
|
||||
|
@ -468,6 +614,12 @@ class GeoJSONWorkerLayer extends Layer {
|
|||
polylineBufferPromises.push(PolylineLayer.SetBufferAttributes(polyline.projected, polyline.options));
|
||||
};
|
||||
|
||||
var point;
|
||||
for (var i = 0; i < points.length; i++) {
|
||||
point = points[i];
|
||||
pointBufferPromises.push(PointLayer.SetBufferAttributes(point.projected, point.options));
|
||||
};
|
||||
|
||||
var data = {};
|
||||
var transferrables = [];
|
||||
|
||||
|
@ -481,9 +633,14 @@ class GeoJSONWorkerLayer extends Layer {
|
|||
data.polylines = result.data;
|
||||
transferrables = transferrables.concat(result.transferrables);
|
||||
|
||||
resolve({
|
||||
data: data,
|
||||
transferrables: transferrables
|
||||
GeoJSONWorkerLayer.ProcessPoints(pointBufferPromises, points, _properties).then((result) => {
|
||||
data.points = result.data;
|
||||
transferrables = transferrables.concat(result.transferrables);
|
||||
|
||||
resolve({
|
||||
data: data,
|
||||
transferrables: transferrables
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -655,6 +812,92 @@ class GeoJSONWorkerLayer extends Layer {
|
|||
});
|
||||
}
|
||||
|
||||
// TODO: Dedupe with ProcessPolygons as they are identical
|
||||
static ProcessPoints(pointPromises, points, _properties) {
|
||||
return new Promise((resolve, reject) => {
|
||||
Promise.all(pointPromises).then((results) => {
|
||||
var transferrables = [];
|
||||
|
||||
var positions = [];
|
||||
var normals = [];
|
||||
var colors = [];
|
||||
|
||||
var properties = [];
|
||||
|
||||
var flats = [];
|
||||
var point;
|
||||
|
||||
var result;
|
||||
for (var i = 0; i < results.length; i++) {
|
||||
result = results[i];
|
||||
|
||||
point = points[i];
|
||||
|
||||
// WORKERS: Making this a typed array will speed up transfer time
|
||||
// As things stand this adds on a few milliseconds
|
||||
flats.push(result.flat);
|
||||
|
||||
// WORKERS: result.attributes is actually an array of polygons for each
|
||||
// feature, though the current logic isn't keeping these all together
|
||||
|
||||
var attributes;
|
||||
for (var j = 0; j < result.attributes.length; j++) {
|
||||
attributes = result.attributes[j];
|
||||
|
||||
positions.push(attributes.positions);
|
||||
normals.push(attributes.normals);
|
||||
colors.push(attributes.colors);
|
||||
|
||||
if (_properties) {
|
||||
properties.push(Buffer.stringToUint8Array(JSON.stringify(polygon.properties)));
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
var mergedAttributes = {
|
||||
positions: Buffer.mergeFloat32Arrays(positions),
|
||||
normals: Buffer.mergeFloat32Arrays(normals),
|
||||
colors: Buffer.mergeFloat32Arrays(colors)
|
||||
};
|
||||
|
||||
transferrables.push(mergedAttributes.positions[0].buffer);
|
||||
transferrables.push(mergedAttributes.positions[1].buffer);
|
||||
|
||||
transferrables.push(mergedAttributes.normals[0].buffer);
|
||||
transferrables.push(mergedAttributes.normals[1].buffer);
|
||||
|
||||
transferrables.push(mergedAttributes.colors[0].buffer);
|
||||
transferrables.push(mergedAttributes.colors[1].buffer);
|
||||
|
||||
var mergedProperties;
|
||||
if (_properties) {
|
||||
mergedProperties = Buffer.mergeUint8Arrays(properties);
|
||||
|
||||
transferrables.push(mergedProperties[0].buffer);
|
||||
transferrables.push(mergedProperties[1].buffer);
|
||||
}
|
||||
|
||||
var output = {
|
||||
attributes: mergedAttributes,
|
||||
flats: flats
|
||||
};
|
||||
|
||||
if (_properties) {
|
||||
output.properties = mergedProperties;
|
||||
}
|
||||
|
||||
// TODO: Also return GeoJSON features that can be mapped to objects on
|
||||
// the main thread. Allow user to provide filter / toggles to only return
|
||||
// properties from the GeoJSON that they need (eg. don't return geometry,
|
||||
// or don't return properties.height)
|
||||
resolve({
|
||||
data: output,
|
||||
transferrables: transferrables
|
||||
});
|
||||
}).catch(reject);
|
||||
});
|
||||
}
|
||||
|
||||
static ProcessGeoJSON(geojson, headers) {
|
||||
if (typeof geojson === 'string') {
|
||||
return GeoJSONWorkerLayer.RequestGeoJSON(geojson, headers);
|
||||
|
@ -683,6 +926,10 @@ class GeoJSONWorkerLayer extends Layer {
|
|||
return PolylineLayer.SetMesh(attributes, attributeLengths, flat, style, this._options);
|
||||
}
|
||||
|
||||
_setPointMesh(attributes, attributeLengths, style, flat) {
|
||||
return PointLayer.SetMesh(attributes, attributeLengths, flat, style, this._options, this._world._environment._skybox);
|
||||
}
|
||||
|
||||
// Set up and re-emit interaction events
|
||||
_addPicking(pickingId, properties) {
|
||||
this._world.on('pick-click-' + pickingId, (pickingId, point2d, point3d, intersects) => {
|
||||
|
|
|
@ -164,8 +164,8 @@ class PointLayer extends Layer {
|
|||
// Debug geometry for points is a thin bar
|
||||
//
|
||||
// TODO: Allow point geometry to be customised / overridden
|
||||
var geometryWidth = Geo.metresToWorld(25, this._pointScale);
|
||||
var geometryHeight = Geo.metresToWorld(200, this._pointScale);
|
||||
var geometryWidth = Geo.metresToWorld(25, options.pointScale);
|
||||
var geometryHeight = Geo.metresToWorld(200, options.pointScale);
|
||||
var _geometry = new THREE.BoxGeometry(geometryWidth, geometryHeight, geometryWidth);
|
||||
|
||||
// Shift geometry up so it sits on the ground
|
||||
|
|
Ładowanie…
Reference in New Issue