2016-09-01 14:50:51 +00:00
|
|
|
import Layer from './Layer';
|
|
|
|
import extend from 'lodash.assign';
|
|
|
|
import reqwest from 'reqwest';
|
|
|
|
import GeoJSON from '../util/GeoJSON';
|
|
|
|
import Worker from '../util/Worker';
|
|
|
|
import Buffer from '../util/Buffer';
|
|
|
|
import Stringify from '../util/Stringify';
|
|
|
|
import PolygonLayer from './geometry/PolygonLayer';
|
2016-09-08 15:32:26 +00:00
|
|
|
import PolylineLayer from './geometry/PolylineLayer';
|
2016-09-09 10:39:16 +00:00
|
|
|
import PointLayer from './geometry/PointLayer';
|
2016-09-01 14:50:51 +00:00
|
|
|
import {latLon as LatLon} from '../geo/LatLon';
|
|
|
|
import {point as Point} from '../geo/Point';
|
|
|
|
import Geo from '../geo/Geo';
|
|
|
|
import PickingMaterial from '../engine/PickingMaterial';
|
|
|
|
|
2016-09-09 11:36:02 +00:00
|
|
|
// TODO: Allow filter method to be run inside a worker to improve performance
|
|
|
|
// TODO: Allow onEachFeature method to be run inside a worker to improve performance
|
|
|
|
|
2016-09-01 14:50:51 +00:00
|
|
|
class GeoJSONWorkerLayer extends Layer {
|
|
|
|
constructor(geojson, options) {
|
|
|
|
var defaults = {
|
|
|
|
topojson: false,
|
|
|
|
style: GeoJSON.defaultStyle,
|
|
|
|
onEachFeature: null,
|
|
|
|
onEachFeatureWorker: null,
|
|
|
|
onAddAttributes: null,
|
|
|
|
interactive: false,
|
2016-09-09 12:15:21 +00:00
|
|
|
pointGeometry: null,
|
2016-09-01 14:50:51 +00:00
|
|
|
onClick: null,
|
|
|
|
headers: {}
|
|
|
|
};
|
|
|
|
|
|
|
|
var _options = extend({}, defaults, options);
|
|
|
|
|
|
|
|
if (typeof options.style === 'object') {
|
|
|
|
_options.style = extend({}, defaults.style, options.style);
|
|
|
|
}
|
|
|
|
|
|
|
|
super(_options);
|
|
|
|
|
2016-10-06 09:11:32 +00:00
|
|
|
this._aborted = false;
|
2016-09-01 14:50:51 +00:00
|
|
|
this._geojson = geojson;
|
|
|
|
}
|
|
|
|
|
|
|
|
_onAdd(world) {
|
|
|
|
if (this._options.interactive) {
|
|
|
|
// Worker layer always controls output to add a picking mesh
|
|
|
|
this._pickingMesh = new THREE.Object3D();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Process GeoJSON
|
|
|
|
return this._process(this._geojson);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Use workers to request and process GeoJSON, returning data structure
|
|
|
|
// containing geometry and any supplementary data for output
|
|
|
|
_process(_geojson) {
|
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
var style = this._options.style;
|
|
|
|
|
2016-09-09 12:15:21 +00:00
|
|
|
// TODO: Convert to buffer and use transferrable objects
|
2016-09-01 14:50:51 +00:00
|
|
|
if (typeof this._options.style === 'function') {
|
|
|
|
style = Stringify.functionToString(this._options.style);
|
|
|
|
}
|
|
|
|
|
2016-09-09 12:15:21 +00:00
|
|
|
var pointGeometry = this._options.pointGeometry;
|
|
|
|
|
|
|
|
// TODO: Convert to buffer and use transferrable objects
|
|
|
|
if (typeof this._options.pointGeometry === 'function') {
|
|
|
|
pointGeometry = Stringify.functionToString(this._options.pointGeometry);
|
|
|
|
}
|
|
|
|
|
2016-09-01 14:50:51 +00:00
|
|
|
var geojson = _geojson;
|
|
|
|
var transferrables = [];
|
|
|
|
|
|
|
|
if (typeof geojson !== 'string') {
|
|
|
|
this._geojson = geojson = Buffer.stringToUint8Array(JSON.stringify(geojson));
|
|
|
|
transferrables.push(geojson.buffer);
|
2016-09-09 12:15:21 +00:00
|
|
|
this._execWorker(geojson, this._options.topojson, this._world._originPoint, style, this._options.interactive, pointGeometry, transferrables).then(() => {
|
2016-09-01 14:50:51 +00:00
|
|
|
resolve();
|
2016-09-05 15:35:54 +00:00
|
|
|
}).catch(reject);
|
2016-09-09 11:36:02 +00:00
|
|
|
} else if (typeof this._options.filter === 'function' || typeof this._options.onEachFeature === 'function') {
|
2016-09-01 14:50:51 +00:00
|
|
|
GeoJSONWorkerLayer.RequestGeoJSON(geojson).then((res) => {
|
2016-10-06 09:11:32 +00:00
|
|
|
// if (this._aborted) {
|
|
|
|
// resolve();
|
|
|
|
// return;
|
|
|
|
// }
|
|
|
|
|
2016-09-01 14:50:51 +00:00
|
|
|
var fc = GeoJSON.collectFeatures(res, this._options.topojson);
|
|
|
|
var features = fc.features;
|
|
|
|
|
2016-09-09 11:36:02 +00:00
|
|
|
// Run filter, if provided
|
|
|
|
if (this._options.filter) {
|
|
|
|
fc.features = features.filter(this._options.filter);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (this._options.onEachFeature) {
|
|
|
|
var feature;
|
|
|
|
for (var i = 0; i < features.length; i++) {
|
|
|
|
feature = features[i];
|
|
|
|
this._options.onEachFeature(feature);
|
|
|
|
};
|
|
|
|
}
|
2016-09-01 14:50:51 +00:00
|
|
|
|
|
|
|
this._geojson = geojson = Buffer.stringToUint8Array(JSON.stringify(fc));
|
|
|
|
transferrables.push(geojson.buffer);
|
|
|
|
|
2016-09-09 12:15:21 +00:00
|
|
|
this._execWorker(geojson, false, this._options.headers, this._world._originPoint, style, this._options.interactive, pointGeometry, transferrables).then(() => {
|
2016-09-01 14:50:51 +00:00
|
|
|
resolve();
|
2016-09-05 15:35:54 +00:00
|
|
|
}).catch(reject);
|
2016-09-01 14:50:51 +00:00
|
|
|
});
|
|
|
|
} else {
|
2016-09-09 12:15:21 +00:00
|
|
|
this._execWorker(geojson, this._options.topojson, this._options.headers, this._world._originPoint, style, this._options.interactive, pointGeometry, transferrables).then(() => {
|
2016-09-01 14:50:51 +00:00
|
|
|
resolve();
|
2016-09-05 15:35:54 +00:00
|
|
|
}).catch(reject);
|
2016-09-01 14:50:51 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2016-09-09 12:15:21 +00:00
|
|
|
_execWorker(geojson, topojson, headers, originPoint, style, interactive, pointGeometry, transferrables) {
|
2016-09-01 14:50:51 +00:00
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
console.time('Worker round trip');
|
|
|
|
|
2016-09-09 12:15:21 +00:00
|
|
|
Worker.exec('GeoJSONWorkerLayer.Process', [geojson, topojson, headers, originPoint, style, interactive, pointGeometry], transferrables).then((results) => {
|
2016-09-01 14:50:51 +00:00
|
|
|
console.timeEnd('Worker round trip');
|
|
|
|
|
2016-10-06 09:11:32 +00:00
|
|
|
// if (this._aborted) {
|
|
|
|
// resolve();
|
|
|
|
// return;
|
|
|
|
// }
|
|
|
|
|
|
|
|
var processPromises = [];
|
|
|
|
|
2016-09-08 16:17:49 +00:00
|
|
|
if (results.polygons) {
|
2016-10-06 09:11:32 +00:00
|
|
|
processPromises.push(this._processPolygonResults(results.polygons));
|
2016-09-08 16:17:49 +00:00
|
|
|
}
|
2016-09-01 14:50:51 +00:00
|
|
|
|
2016-09-08 16:17:49 +00:00
|
|
|
if (results.polylines) {
|
2016-10-06 09:11:32 +00:00
|
|
|
processPromises.push(this._processPolylineResults(results.polylines));
|
2016-09-01 14:50:51 +00:00
|
|
|
}
|
|
|
|
|
2016-09-09 10:39:16 +00:00
|
|
|
if (results.points) {
|
2016-10-06 09:11:32 +00:00
|
|
|
processPromises.push(this._processPointResults(results.points));
|
2016-09-09 10:39:16 +00:00
|
|
|
}
|
|
|
|
|
2016-10-06 09:11:32 +00:00
|
|
|
if (processPromises.length > 0) {
|
|
|
|
Promise.all(processPromises).then(() => {
|
|
|
|
resolve();
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
resolve();
|
|
|
|
}
|
2016-09-08 16:17:49 +00:00
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
2016-09-01 14:50:51 +00:00
|
|
|
|
2016-09-08 16:17:49 +00:00
|
|
|
// TODO: Dedupe with polyline method
|
|
|
|
_processPolygonResults(results) {
|
2016-10-06 09:11:32 +00:00
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
var splitPositions = Buffer.splitFloat32Array(results.attributes.positions);
|
|
|
|
var splitNormals = Buffer.splitFloat32Array(results.attributes.normals);
|
|
|
|
var splitColors = Buffer.splitFloat32Array(results.attributes.colors);
|
|
|
|
var splitTops = Buffer.splitFloat32Array(results.attributes.tops);
|
2016-09-01 14:50:51 +00:00
|
|
|
|
2016-10-06 09:11:32 +00:00
|
|
|
var splitOutlinePositions;
|
|
|
|
var splitOutlineColors;
|
2016-09-13 10:09:01 +00:00
|
|
|
|
2016-10-06 09:11:32 +00:00
|
|
|
if (results.outlineAttributes) {
|
|
|
|
splitOutlinePositions = Buffer.splitFloat32Array(results.outlineAttributes.positions);
|
|
|
|
splitOutlineColors = Buffer.splitFloat32Array(results.outlineAttributes.colors);
|
|
|
|
}
|
2016-09-13 10:09:01 +00:00
|
|
|
|
2016-10-06 09:11:32 +00:00
|
|
|
var splitProperties;
|
|
|
|
if (results.properties) {
|
|
|
|
splitProperties = Buffer.splitUint8Array(results.properties);
|
|
|
|
}
|
2016-09-01 14:50:51 +00:00
|
|
|
|
2016-10-06 09:11:32 +00:00
|
|
|
var flats = results.flats;
|
2016-09-01 14:50:51 +00:00
|
|
|
|
2016-10-06 09:11:32 +00:00
|
|
|
var objects = [];
|
|
|
|
var outlineObjects = [];
|
2016-09-13 10:09:01 +00:00
|
|
|
|
2016-10-06 09:11:32 +00:00
|
|
|
var obj;
|
|
|
|
var pickingId;
|
|
|
|
var pickingIds;
|
|
|
|
var properties;
|
2016-09-01 14:50:51 +00:00
|
|
|
|
2016-10-06 09:11:32 +00:00
|
|
|
var polygonAttributeLengths = {
|
|
|
|
positions: 3,
|
|
|
|
normals: 3,
|
|
|
|
colors: 3,
|
|
|
|
tops: 1
|
|
|
|
};
|
2016-09-01 14:50:51 +00:00
|
|
|
|
2016-10-06 09:11:32 +00:00
|
|
|
var polygonOutlineAttributeLengths = {
|
|
|
|
positions: 3,
|
|
|
|
colors: 3
|
|
|
|
};
|
2016-09-13 10:09:01 +00:00
|
|
|
|
2016-10-06 09:11:32 +00:00
|
|
|
for (var i = 0; i < splitPositions.length; i++) {
|
|
|
|
if (splitProperties && splitProperties[i]) {
|
|
|
|
properties = JSON.parse(Buffer.uint8ArrayToString(splitProperties[i]));
|
|
|
|
} else {
|
|
|
|
properties = {};
|
|
|
|
}
|
2016-09-01 14:50:51 +00:00
|
|
|
|
2016-10-06 09:11:32 +00:00
|
|
|
// 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],
|
|
|
|
tops: splitTops[i]
|
|
|
|
}],
|
|
|
|
properties: properties,
|
|
|
|
flat: flats[i]
|
|
|
|
};
|
2016-09-01 14:50:51 +00:00
|
|
|
|
2016-10-06 09:11:32 +00:00
|
|
|
// 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();
|
2016-09-01 14:50:51 +00:00
|
|
|
|
2016-10-06 09:11:32 +00:00
|
|
|
pickingIds = new Float32Array(splitPositions[i].length / 3);
|
|
|
|
pickingIds.fill(pickingId);
|
2016-09-01 14:50:51 +00:00
|
|
|
|
2016-10-06 09:11:32 +00:00
|
|
|
obj.attributes[0].pickingIds = pickingIds;
|
2016-09-01 14:50:51 +00:00
|
|
|
|
2016-10-06 09:11:32 +00:00
|
|
|
polygonAttributeLengths.pickingIds = 1;
|
2016-09-08 16:17:49 +00:00
|
|
|
|
2016-10-06 09:11:32 +00:00
|
|
|
this._addPicking(pickingId, properties);
|
|
|
|
}
|
2016-09-08 16:17:49 +00:00
|
|
|
|
2016-10-06 09:11:32 +00:00
|
|
|
// 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;
|
|
|
|
polygonAttributeLengths[key] = customAttribute.length;
|
|
|
|
}
|
2016-09-01 14:50:51 +00:00
|
|
|
}
|
2016-10-06 09:11:32 +00:00
|
|
|
|
|
|
|
objects.push(obj);
|
2016-09-08 16:17:49 +00:00
|
|
|
}
|
2016-09-01 14:50:51 +00:00
|
|
|
|
2016-10-06 09:11:32 +00:00
|
|
|
for (var i = 0; i < splitOutlinePositions.length; i++) {
|
|
|
|
obj = {
|
|
|
|
attributes: [{
|
|
|
|
positions: splitOutlinePositions[i],
|
|
|
|
colors: splitOutlineColors[i]
|
|
|
|
}],
|
|
|
|
flat: true
|
|
|
|
};
|
|
|
|
|
|
|
|
outlineObjects.push(obj);
|
|
|
|
}
|
|
|
|
|
|
|
|
var polygonAttributes = [];
|
|
|
|
var polygonOutlineAttributes = [];
|
|
|
|
|
|
|
|
var polygonFlat = true;
|
|
|
|
|
|
|
|
for (var i = 0; i < objects.length; i++) {
|
|
|
|
obj = objects[i];
|
2016-09-01 14:50:51 +00:00
|
|
|
|
2016-10-07 10:46:52 +00:00
|
|
|
// TODO: Work out why obj.flat is rarely set to something other than
|
|
|
|
// true or false. Potentially undefined.
|
|
|
|
if (polygonFlat && obj.flat === false) {
|
2016-10-06 09:11:32 +00:00
|
|
|
polygonFlat = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
var bufferAttributes = Buffer.mergeAttributes(obj.attributes);
|
|
|
|
polygonAttributes.push(bufferAttributes);
|
2016-09-13 10:09:01 +00:00
|
|
|
};
|
|
|
|
|
2016-10-06 09:11:32 +00:00
|
|
|
for (var i = 0; i < outlineObjects.length; i++) {
|
|
|
|
obj = outlineObjects[i];
|
2016-09-13 10:09:01 +00:00
|
|
|
|
2016-10-06 09:11:32 +00:00
|
|
|
var bufferAttributes = Buffer.mergeAttributes(obj.attributes);
|
|
|
|
polygonOutlineAttributes.push(bufferAttributes);
|
|
|
|
};
|
2016-09-01 14:50:51 +00:00
|
|
|
|
2016-10-06 09:11:32 +00:00
|
|
|
var outputPromises = [];
|
2016-09-01 14:50:51 +00:00
|
|
|
|
2016-10-06 11:40:09 +00:00
|
|
|
var style;
|
|
|
|
|
2016-10-06 09:11:32 +00:00
|
|
|
if (polygonAttributes.length > 0) {
|
|
|
|
var mergedPolygonAttributes = Buffer.mergeAttributes(polygonAttributes);
|
2016-09-01 14:50:51 +00:00
|
|
|
|
2016-10-06 09:11:32 +00:00
|
|
|
// TODO: Make this work when style is a function per feature
|
2016-10-06 11:40:09 +00:00
|
|
|
style = (typeof this._options.style === 'function') ? this._options.style(objects[0]) : this._options.style;
|
2016-10-06 09:11:32 +00:00
|
|
|
style = extend({}, GeoJSON.defaultStyle, style);
|
2016-09-01 14:50:51 +00:00
|
|
|
|
2016-10-06 09:11:32 +00:00
|
|
|
outputPromises.push(this._setPolygonMesh(mergedPolygonAttributes, polygonAttributeLengths, style, polygonFlat));
|
|
|
|
}
|
2016-09-01 14:50:51 +00:00
|
|
|
|
2016-10-06 09:11:32 +00:00
|
|
|
if (polygonOutlineAttributes.length > 0) {
|
|
|
|
var mergedPolygonOutlineAttributes = Buffer.mergeAttributes(polygonOutlineAttributes);
|
2016-09-13 10:09:01 +00:00
|
|
|
|
2016-10-06 11:40:09 +00:00
|
|
|
style = (typeof this._options.style === 'function') ? this._options.style(objects[0]) : this._options.style;
|
2016-10-06 09:11:32 +00:00
|
|
|
style = extend({}, GeoJSON.defaultStyle, style);
|
2016-09-13 10:09:01 +00:00
|
|
|
|
2016-10-06 11:40:09 +00:00
|
|
|
if (style.outlineRenderOrder !== undefined) {
|
|
|
|
style.lineRenderOrder = style.outlineRenderOrder;
|
|
|
|
} else {
|
2016-10-07 10:46:52 +00:00
|
|
|
style.lineRenderOrder = (style.renderOrder) ? style.renderOrder + 1 : 4;
|
2016-10-06 11:40:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (style.outlineWidth) {
|
|
|
|
style.lineWidth = style.outlineWidth;
|
|
|
|
}
|
|
|
|
|
2016-10-06 09:11:32 +00:00
|
|
|
outputPromises.push(this._setPolylineMesh(mergedPolygonOutlineAttributes, polygonOutlineAttributeLengths, style, true));
|
|
|
|
}
|
2016-09-01 14:50:51 +00:00
|
|
|
|
2016-10-06 09:11:32 +00:00
|
|
|
Promise.all(outputPromises).then((results) => {
|
|
|
|
var [polygonResult, outlineResult] = results;
|
2016-09-08 13:02:55 +00:00
|
|
|
|
2016-10-06 09:11:32 +00:00
|
|
|
if (polygonResult) {
|
|
|
|
this._polygonMesh = polygonResult.mesh;
|
|
|
|
this.add(this._polygonMesh);
|
2016-09-08 16:17:49 +00:00
|
|
|
|
2016-10-06 09:11:32 +00:00
|
|
|
if (polygonResult.pickingMesh) {
|
|
|
|
this._pickingMesh.add(polygonResult.pickingMesh);
|
|
|
|
}
|
2016-09-01 14:50:51 +00:00
|
|
|
}
|
2016-09-13 10:09:01 +00:00
|
|
|
|
2016-10-06 09:11:32 +00:00
|
|
|
if (outlineResult) {
|
|
|
|
this.add(outlineResult.mesh);
|
|
|
|
}
|
2016-09-13 10:09:01 +00:00
|
|
|
|
2016-10-06 09:11:32 +00:00
|
|
|
resolve();
|
2016-09-13 10:09:01 +00:00
|
|
|
});
|
2016-10-06 09:11:32 +00:00
|
|
|
});
|
2016-09-08 16:17:49 +00:00
|
|
|
}
|
2016-09-01 14:50:51 +00:00
|
|
|
|
2016-09-08 16:17:49 +00:00
|
|
|
// TODO: Dedupe with polygon method
|
|
|
|
_processPolylineResults(results) {
|
2016-10-06 09:11:32 +00:00
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
var splitPositions = Buffer.splitFloat32Array(results.attributes.positions);
|
|
|
|
var splitColors = Buffer.splitFloat32Array(results.attributes.colors);
|
2016-09-08 16:17:49 +00:00
|
|
|
|
2016-10-06 09:11:32 +00:00
|
|
|
var splitProperties;
|
|
|
|
if (results.properties) {
|
|
|
|
splitProperties = Buffer.splitUint8Array(results.properties);
|
|
|
|
}
|
2016-09-08 16:17:49 +00:00
|
|
|
|
2016-10-06 09:11:32 +00:00
|
|
|
var flats = results.flats;
|
2016-09-08 16:17:49 +00:00
|
|
|
|
2016-10-06 09:11:32 +00:00
|
|
|
var objects = [];
|
|
|
|
var obj;
|
|
|
|
var pickingId;
|
|
|
|
var pickingIds;
|
|
|
|
var properties;
|
2016-09-08 16:17:49 +00:00
|
|
|
|
2016-10-06 09:11:32 +00:00
|
|
|
var polylineAttributeLengths = {
|
|
|
|
positions: 3,
|
|
|
|
colors: 3
|
|
|
|
};
|
2016-09-08 16:17:49 +00:00
|
|
|
|
2016-10-06 09:11:32 +00:00
|
|
|
for (var i = 0; i < splitPositions.length; i++) {
|
|
|
|
if (splitProperties && splitProperties[i]) {
|
|
|
|
properties = JSON.parse(Buffer.uint8ArrayToString(splitProperties[i]));
|
|
|
|
} else {
|
|
|
|
properties = {};
|
|
|
|
}
|
2016-09-08 16:17:49 +00:00
|
|
|
|
2016-10-06 09:11:32 +00:00
|
|
|
// 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],
|
|
|
|
colors: splitColors[i]
|
|
|
|
}],
|
|
|
|
properties: properties,
|
|
|
|
flat: flats[i]
|
|
|
|
};
|
2016-09-08 16:17:49 +00:00
|
|
|
|
2016-10-06 09:11:32 +00:00
|
|
|
// 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();
|
2016-09-08 16:17:49 +00:00
|
|
|
|
2016-10-06 09:11:32 +00:00
|
|
|
pickingIds = new Float32Array(splitPositions[i].length / 3);
|
|
|
|
pickingIds.fill(pickingId);
|
2016-09-08 16:17:49 +00:00
|
|
|
|
2016-10-06 09:11:32 +00:00
|
|
|
obj.attributes[0].pickingIds = pickingIds;
|
2016-09-08 16:17:49 +00:00
|
|
|
|
2016-10-06 09:11:32 +00:00
|
|
|
polylineAttributeLengths.pickingIds = 1;
|
2016-09-08 16:17:49 +00:00
|
|
|
|
2016-10-06 09:11:32 +00:00
|
|
|
this._addPicking(pickingId, properties);
|
|
|
|
}
|
2016-09-08 16:17:49 +00:00
|
|
|
|
2016-10-06 09:11:32 +00:00
|
|
|
// TODO: Make this specific to polyline 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;
|
|
|
|
polylineAttributeLengths[key] = customAttribute.length;
|
|
|
|
}
|
2016-09-08 16:17:49 +00:00
|
|
|
}
|
2016-10-06 09:11:32 +00:00
|
|
|
|
|
|
|
objects.push(obj);
|
2016-09-08 16:17:49 +00:00
|
|
|
}
|
|
|
|
|
2016-10-06 09:11:32 +00:00
|
|
|
var polylineAttributes = [];
|
2016-09-08 16:17:49 +00:00
|
|
|
|
2016-10-06 09:11:32 +00:00
|
|
|
var polylineFlat = true;
|
2016-09-08 16:17:49 +00:00
|
|
|
|
2016-10-06 09:11:32 +00:00
|
|
|
for (var i = 0; i < objects.length; i++) {
|
|
|
|
obj = objects[i];
|
2016-09-08 16:17:49 +00:00
|
|
|
|
2016-10-06 09:11:32 +00:00
|
|
|
if (polylineFlat && !obj.flat) {
|
|
|
|
polylineFlat = false;
|
|
|
|
}
|
2016-09-08 16:17:49 +00:00
|
|
|
|
2016-10-06 09:11:32 +00:00
|
|
|
var bufferAttributes = Buffer.mergeAttributes(obj.attributes);
|
|
|
|
polylineAttributes.push(bufferAttributes);
|
|
|
|
};
|
2016-09-08 16:17:49 +00:00
|
|
|
|
2016-10-06 09:11:32 +00:00
|
|
|
if (polylineAttributes.length > 0) {
|
|
|
|
var mergedPolylineAttributes = Buffer.mergeAttributes(polylineAttributes);
|
2016-09-08 16:17:49 +00:00
|
|
|
|
2016-10-06 09:11:32 +00:00
|
|
|
// 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);
|
2016-09-08 16:17:49 +00:00
|
|
|
|
2016-10-06 09:11:32 +00:00
|
|
|
this._setPolylineMesh(mergedPolylineAttributes, polylineAttributeLengths, style, polylineFlat).then((result) => {
|
|
|
|
this._polylineMesh = result.mesh;
|
|
|
|
this.add(this._polylineMesh);
|
2016-09-08 16:17:49 +00:00
|
|
|
|
2016-10-06 09:11:32 +00:00
|
|
|
if (result.pickingMesh) {
|
|
|
|
this._pickingMesh.add(result.pickingMesh);
|
|
|
|
}
|
2016-09-08 16:17:49 +00:00
|
|
|
|
2016-10-06 09:11:32 +00:00
|
|
|
resolve();
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
resolve();
|
|
|
|
}
|
|
|
|
});
|
2016-09-01 14:50:51 +00:00
|
|
|
}
|
|
|
|
|
2016-09-09 10:39:16 +00:00
|
|
|
_processPointResults(results) {
|
2016-10-06 09:11:32 +00:00
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
var splitPositions = Buffer.splitFloat32Array(results.attributes.positions);
|
|
|
|
var splitNormals = Buffer.splitFloat32Array(results.attributes.normals);
|
|
|
|
var splitColors = Buffer.splitFloat32Array(results.attributes.colors);
|
2016-09-09 10:39:16 +00:00
|
|
|
|
2016-10-06 09:11:32 +00:00
|
|
|
var splitProperties;
|
|
|
|
if (results.properties) {
|
|
|
|
splitProperties = Buffer.splitUint8Array(results.properties);
|
|
|
|
}
|
2016-09-09 10:39:16 +00:00
|
|
|
|
2016-10-06 09:11:32 +00:00
|
|
|
var flats = results.flats;
|
2016-09-09 10:39:16 +00:00
|
|
|
|
2016-10-06 09:11:32 +00:00
|
|
|
var objects = [];
|
|
|
|
var obj;
|
|
|
|
var pickingId;
|
|
|
|
var pickingIds;
|
|
|
|
var properties;
|
2016-09-09 10:39:16 +00:00
|
|
|
|
2016-10-06 09:11:32 +00:00
|
|
|
var pointAttributeLengths = {
|
|
|
|
positions: 3,
|
|
|
|
normals: 3,
|
|
|
|
colors: 3
|
|
|
|
};
|
2016-09-09 10:39:16 +00:00
|
|
|
|
2016-10-06 09:11:32 +00:00
|
|
|
for (var i = 0; i < splitPositions.length; i++) {
|
|
|
|
if (splitProperties && splitProperties[i]) {
|
|
|
|
properties = JSON.parse(Buffer.uint8ArrayToString(splitProperties[i]));
|
|
|
|
} else {
|
|
|
|
properties = {};
|
|
|
|
}
|
2016-09-09 10:39:16 +00:00
|
|
|
|
2016-10-06 09:11:32 +00:00
|
|
|
// 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]
|
|
|
|
};
|
2016-09-09 10:39:16 +00:00
|
|
|
|
2016-10-06 09:11:32 +00:00
|
|
|
// 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();
|
2016-09-09 10:39:16 +00:00
|
|
|
|
2016-10-06 09:11:32 +00:00
|
|
|
pickingIds = new Float32Array(splitPositions[i].length / 3);
|
|
|
|
pickingIds.fill(pickingId);
|
2016-09-09 10:39:16 +00:00
|
|
|
|
2016-10-06 09:11:32 +00:00
|
|
|
obj.attributes[0].pickingIds = pickingIds;
|
2016-09-09 10:39:16 +00:00
|
|
|
|
2016-10-06 09:11:32 +00:00
|
|
|
pointAttributeLengths.pickingIds = 1;
|
2016-09-09 10:39:16 +00:00
|
|
|
|
2016-10-06 09:11:32 +00:00
|
|
|
this._addPicking(pickingId, properties);
|
|
|
|
}
|
2016-09-09 10:39:16 +00:00
|
|
|
|
2016-10-06 09:11:32 +00:00
|
|
|
// 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;
|
|
|
|
}
|
2016-09-09 10:39:16 +00:00
|
|
|
}
|
2016-10-06 09:11:32 +00:00
|
|
|
|
|
|
|
objects.push(obj);
|
2016-09-09 10:39:16 +00:00
|
|
|
}
|
|
|
|
|
2016-10-06 09:11:32 +00:00
|
|
|
var pointAttributes = [];
|
2016-09-09 10:39:16 +00:00
|
|
|
|
2016-10-06 09:11:32 +00:00
|
|
|
var pointFlat = true;
|
2016-09-09 10:39:16 +00:00
|
|
|
|
2016-10-06 09:11:32 +00:00
|
|
|
for (var i = 0; i < objects.length; i++) {
|
|
|
|
obj = objects[i];
|
2016-09-09 10:39:16 +00:00
|
|
|
|
2016-10-06 09:11:32 +00:00
|
|
|
if (pointFlat && !obj.flat) {
|
|
|
|
pointFlat = false;
|
|
|
|
}
|
2016-09-09 10:39:16 +00:00
|
|
|
|
2016-10-06 09:11:32 +00:00
|
|
|
var bufferAttributes = Buffer.mergeAttributes(obj.attributes);
|
|
|
|
pointAttributes.push(bufferAttributes);
|
|
|
|
};
|
2016-09-09 10:39:16 +00:00
|
|
|
|
2016-10-06 09:11:32 +00:00
|
|
|
if (pointAttributes.length > 0) {
|
|
|
|
var mergedPointAttributes = Buffer.mergeAttributes(pointAttributes);
|
2016-09-09 10:39:16 +00:00
|
|
|
|
2016-10-06 09:11:32 +00:00
|
|
|
// 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);
|
2016-09-09 10:39:16 +00:00
|
|
|
|
2016-10-06 09:11:32 +00:00
|
|
|
this._setPointMesh(mergedPointAttributes, pointAttributeLengths, style, pointFlat).then((result) => {
|
|
|
|
this._pointMesh = result.mesh;
|
|
|
|
this.add(this._pointMesh);
|
2016-09-09 10:39:16 +00:00
|
|
|
|
2016-10-06 09:11:32 +00:00
|
|
|
if (result.pickingMesh) {
|
|
|
|
this._pickingMesh.add(result.pickingMesh);
|
|
|
|
}
|
2016-09-09 10:39:16 +00:00
|
|
|
|
2016-10-06 09:11:32 +00:00
|
|
|
resolve();
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
resolve();
|
|
|
|
}
|
|
|
|
});
|
2016-09-09 10:39:16 +00:00
|
|
|
}
|
|
|
|
|
2016-09-01 14:50:51 +00:00
|
|
|
// 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
|
|
|
|
// and properties
|
|
|
|
//
|
|
|
|
// 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
|
2016-09-09 10:39:16 +00:00
|
|
|
//
|
|
|
|
// TODO: Support passing custom geometry for point layers
|
2016-09-09 12:15:21 +00:00
|
|
|
static Process(geojson, topojson, headers, originPoint, _style, _properties, _pointGeometry) {
|
2016-09-01 14:50:51 +00:00
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
GeoJSONWorkerLayer.ProcessGeoJSON(geojson, headers).then((res) => {
|
|
|
|
// Collects features into a single FeatureCollection
|
|
|
|
//
|
|
|
|
// Also converts TopoJSON to GeoJSON if instructed
|
|
|
|
var geojson = GeoJSON.collectFeatures(res, topojson);
|
|
|
|
|
|
|
|
// TODO: Check that GeoJSON is valid / usable
|
|
|
|
|
|
|
|
var features = geojson.features;
|
|
|
|
|
|
|
|
// TODO: Run filter, if provided (must be static)
|
|
|
|
|
|
|
|
var pointScale;
|
|
|
|
var polygons = [];
|
2016-09-08 15:32:26 +00:00
|
|
|
var polylines = [];
|
2016-09-09 10:39:16 +00:00
|
|
|
var points = [];
|
2016-09-01 14:50:51 +00:00
|
|
|
|
|
|
|
// Deserialise style function if provided
|
|
|
|
if (typeof _style === 'string') {
|
|
|
|
_style = Stringify.stringToFunction(_style);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Assume that a style won't be set per feature
|
|
|
|
var style = _style;
|
|
|
|
|
2016-09-09 12:15:21 +00:00
|
|
|
var pointGeometry;
|
|
|
|
// Deserialise pointGeometry function if provided
|
|
|
|
if (typeof _pointGeometry === 'string') {
|
|
|
|
pointGeometry = Stringify.stringToFunction(_pointGeometry);
|
|
|
|
}
|
|
|
|
|
2016-09-01 14:50:51 +00:00
|
|
|
var feature;
|
|
|
|
for (var i = 0; i < features.length; i++) {
|
|
|
|
feature = features[i];
|
|
|
|
|
|
|
|
var geometry = feature.geometry;
|
|
|
|
var coordinates = (geometry.coordinates) ? geometry.coordinates : null;
|
|
|
|
|
|
|
|
if (!coordinates || !geometry) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get per-feature style object, if provided
|
|
|
|
if (typeof _style === 'function') {
|
|
|
|
style = extend({}, GeoJSON.defaultStyle, _style(feature));
|
|
|
|
// console.log(feature, style);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (geometry.type === 'Polygon' || geometry.type === 'MultiPolygon') {
|
|
|
|
coordinates = (PolygonLayer.isSingle(coordinates)) ? [coordinates] : coordinates;
|
|
|
|
|
|
|
|
var converted = coordinates.map(_coordinates => {
|
|
|
|
return _coordinates.map(ring => {
|
|
|
|
return ring.map(coordinate => {
|
|
|
|
return LatLon(coordinate[1], coordinate[0]);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
var point;
|
2016-09-05 15:35:54 +00:00
|
|
|
var projected = converted.map((_coordinates) => {
|
|
|
|
return _coordinates.map((ring) => {
|
|
|
|
return ring.map((latlon) => {
|
2016-09-01 14:50:51 +00:00
|
|
|
point = Geo.latLonToPoint(latlon)._subtract(originPoint);
|
|
|
|
|
|
|
|
if (!pointScale) {
|
|
|
|
pointScale = Geo.pointScale(latlon);
|
|
|
|
}
|
|
|
|
|
|
|
|
return point;
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
var polygon = {
|
|
|
|
projected: projected,
|
|
|
|
options: {
|
|
|
|
pointScale: pointScale,
|
|
|
|
style: style
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
if (_properties) {
|
|
|
|
polygon.properties = feature.properties;
|
|
|
|
}
|
|
|
|
|
|
|
|
polygons.push(polygon);
|
|
|
|
}
|
|
|
|
|
2016-09-08 15:32:26 +00:00
|
|
|
if (geometry.type === 'LineString' || geometry.type === 'MultiLineString') {
|
|
|
|
coordinates = (PolylineLayer.isSingle(coordinates)) ? [coordinates] : coordinates;
|
|
|
|
|
|
|
|
var converted = coordinates.map(_coordinates => {
|
|
|
|
return _coordinates.map(coordinate => {
|
|
|
|
return LatLon(coordinate[1], coordinate[0]);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
var point;
|
|
|
|
var projected = converted.map((_coordinates) => {
|
|
|
|
return _coordinates.map((latlon) => {
|
|
|
|
point = Geo.latLonToPoint(latlon)._subtract(originPoint);
|
|
|
|
|
|
|
|
if (!pointScale) {
|
|
|
|
pointScale = Geo.pointScale(latlon);
|
|
|
|
}
|
|
|
|
|
|
|
|
return point;
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
var polyline = {
|
|
|
|
projected: projected,
|
|
|
|
options: {
|
|
|
|
pointScale: pointScale,
|
|
|
|
style: style
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
if (_properties) {
|
|
|
|
polyline.properties = feature.properties;
|
|
|
|
}
|
|
|
|
|
|
|
|
polylines.push(polyline);
|
|
|
|
}
|
2016-09-01 14:50:51 +00:00
|
|
|
|
2016-09-09 10:39:16 +00:00
|
|
|
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: {
|
2016-09-09 12:15:21 +00:00
|
|
|
pointGeometry: pointGeometry(feature),
|
2016-09-09 10:39:16 +00:00
|
|
|
pointScale: pointScale,
|
|
|
|
style: style
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
if (_properties) {
|
|
|
|
point.properties = feature.properties;
|
|
|
|
}
|
|
|
|
|
|
|
|
points.push(point);
|
|
|
|
}
|
2016-09-01 14:50:51 +00:00
|
|
|
};
|
|
|
|
|
2016-09-08 15:32:26 +00:00
|
|
|
var polygonBufferPromises = [];
|
|
|
|
var polylineBufferPromises = [];
|
2016-09-09 10:39:16 +00:00
|
|
|
var pointBufferPromises = [];
|
2016-09-01 14:50:51 +00:00
|
|
|
|
|
|
|
var polygon;
|
|
|
|
for (var i = 0; i < polygons.length; i++) {
|
|
|
|
polygon = polygons[i];
|
2016-09-08 15:32:26 +00:00
|
|
|
polygonBufferPromises.push(PolygonLayer.SetBufferAttributes(polygon.projected, polygon.options));
|
2016-09-01 14:50:51 +00:00
|
|
|
};
|
|
|
|
|
2016-09-08 15:32:26 +00:00
|
|
|
var polyline;
|
|
|
|
for (var i = 0; i < polylines.length; i++) {
|
|
|
|
polyline = polylines[i];
|
|
|
|
polylineBufferPromises.push(PolylineLayer.SetBufferAttributes(polyline.projected, polyline.options));
|
|
|
|
};
|
|
|
|
|
2016-09-09 10:39:16 +00:00
|
|
|
var point;
|
|
|
|
for (var i = 0; i < points.length; i++) {
|
|
|
|
point = points[i];
|
|
|
|
pointBufferPromises.push(PointLayer.SetBufferAttributes(point.projected, point.options));
|
|
|
|
};
|
|
|
|
|
2016-09-08 16:17:49 +00:00
|
|
|
var data = {};
|
|
|
|
var transferrables = [];
|
|
|
|
|
2016-09-08 15:32:26 +00:00
|
|
|
// TODO: Make this work with polylines too
|
2016-09-08 16:17:49 +00:00
|
|
|
// TODO: Make this so it's not a nest of promises
|
2016-09-08 15:32:26 +00:00
|
|
|
GeoJSONWorkerLayer.ProcessPolygons(polygonBufferPromises, polygons, _properties).then((result) => {
|
2016-09-08 16:17:49 +00:00
|
|
|
data.polygons = result.data;
|
|
|
|
transferrables = transferrables.concat(result.transferrables);
|
|
|
|
|
|
|
|
GeoJSONWorkerLayer.ProcessPolylines(polylineBufferPromises, polylines, _properties).then((result) => {
|
|
|
|
data.polylines = result.data;
|
|
|
|
transferrables = transferrables.concat(result.transferrables);
|
|
|
|
|
2016-09-09 10:39:16 +00:00
|
|
|
GeoJSONWorkerLayer.ProcessPoints(pointBufferPromises, points, _properties).then((result) => {
|
|
|
|
data.points = result.data;
|
|
|
|
transferrables = transferrables.concat(result.transferrables);
|
|
|
|
|
|
|
|
resolve({
|
|
|
|
data: data,
|
|
|
|
transferrables: transferrables
|
|
|
|
});
|
2016-09-08 16:17:49 +00:00
|
|
|
});
|
2016-09-08 15:32:26 +00:00
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
2016-09-01 14:50:51 +00:00
|
|
|
|
2016-09-08 15:32:26 +00:00
|
|
|
static ProcessPolygons(polygonPromises, polygons, _properties) {
|
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
Promise.all(polygonPromises).then((results) => {
|
|
|
|
var transferrables = [];
|
2016-09-01 14:50:51 +00:00
|
|
|
|
2016-09-08 15:32:26 +00:00
|
|
|
var positions = [];
|
|
|
|
var normals = [];
|
|
|
|
var colors = [];
|
2016-10-03 09:36:10 +00:00
|
|
|
var tops = [];
|
2016-09-01 14:50:51 +00:00
|
|
|
|
2016-09-13 10:09:01 +00:00
|
|
|
var outlinePositions = [];
|
|
|
|
var outlineColors = [];
|
|
|
|
|
2016-09-08 15:32:26 +00:00
|
|
|
var properties = [];
|
2016-09-01 14:50:51 +00:00
|
|
|
|
2016-09-08 15:32:26 +00:00
|
|
|
var flats = [];
|
|
|
|
var polygon;
|
2016-09-01 14:50:51 +00:00
|
|
|
|
2016-09-08 15:32:26 +00:00
|
|
|
var result;
|
|
|
|
for (var i = 0; i < results.length; i++) {
|
|
|
|
result = results[i];
|
2016-09-01 14:50:51 +00:00
|
|
|
|
2016-09-08 15:32:26 +00:00
|
|
|
polygon = polygons[i];
|
2016-09-01 14:50:51 +00:00
|
|
|
|
2016-09-08 15:32:26 +00:00
|
|
|
// WORKERS: Making this a typed array will speed up transfer time
|
|
|
|
// As things stand this adds on a few milliseconds
|
|
|
|
flats.push(result.flat);
|
2016-09-01 14:50:51 +00:00
|
|
|
|
2016-09-08 15:32:26 +00:00
|
|
|
// WORKERS: result.attributes is actually an array of polygons for each
|
|
|
|
// feature, though the current logic isn't keeping these all together
|
2016-09-01 14:50:51 +00:00
|
|
|
|
2016-09-08 15:32:26 +00:00
|
|
|
var attributes;
|
|
|
|
for (var j = 0; j < result.attributes.length; j++) {
|
|
|
|
attributes = result.attributes[j];
|
2016-09-01 14:50:51 +00:00
|
|
|
|
2016-09-08 15:32:26 +00:00
|
|
|
positions.push(attributes.positions);
|
|
|
|
normals.push(attributes.normals);
|
|
|
|
colors.push(attributes.colors);
|
2016-10-03 09:36:10 +00:00
|
|
|
tops.push(attributes.tops);
|
2016-09-01 14:50:51 +00:00
|
|
|
|
2016-09-08 15:32:26 +00:00
|
|
|
if (_properties) {
|
|
|
|
properties.push(Buffer.stringToUint8Array(JSON.stringify(polygon.properties)));
|
|
|
|
}
|
2016-09-01 14:50:51 +00:00
|
|
|
};
|
2016-09-13 10:09:01 +00:00
|
|
|
|
|
|
|
var outlineAttributes;
|
|
|
|
for (var j = 0; j < result.outlineAttributes.length; j++) {
|
|
|
|
outlineAttributes = result.outlineAttributes[j];
|
|
|
|
|
|
|
|
outlinePositions.push(outlineAttributes.positions);
|
|
|
|
outlineColors.push(outlineAttributes.colors);
|
|
|
|
};
|
2016-09-08 15:32:26 +00:00
|
|
|
};
|
2016-09-01 14:50:51 +00:00
|
|
|
|
2016-09-08 15:32:26 +00:00
|
|
|
var mergedAttributes = {
|
|
|
|
positions: Buffer.mergeFloat32Arrays(positions),
|
|
|
|
normals: Buffer.mergeFloat32Arrays(normals),
|
2016-10-03 09:36:10 +00:00
|
|
|
colors: Buffer.mergeFloat32Arrays(colors),
|
|
|
|
tops: Buffer.mergeFloat32Arrays(tops)
|
2016-09-08 15:32:26 +00:00
|
|
|
};
|
2016-09-01 14:50:51 +00:00
|
|
|
|
2016-09-13 10:09:01 +00:00
|
|
|
var mergedOutlineAttributes = {
|
|
|
|
positions: Buffer.mergeFloat32Arrays(outlinePositions),
|
|
|
|
colors: Buffer.mergeFloat32Arrays(outlineColors)
|
|
|
|
};
|
|
|
|
|
2016-09-08 15:32:26 +00:00
|
|
|
transferrables.push(mergedAttributes.positions[0].buffer);
|
|
|
|
transferrables.push(mergedAttributes.positions[1].buffer);
|
2016-09-01 14:50:51 +00:00
|
|
|
|
2016-09-08 15:32:26 +00:00
|
|
|
transferrables.push(mergedAttributes.normals[0].buffer);
|
|
|
|
transferrables.push(mergedAttributes.normals[1].buffer);
|
2016-09-01 14:50:51 +00:00
|
|
|
|
2016-09-08 15:32:26 +00:00
|
|
|
transferrables.push(mergedAttributes.colors[0].buffer);
|
|
|
|
transferrables.push(mergedAttributes.colors[1].buffer);
|
2016-09-01 14:50:51 +00:00
|
|
|
|
2016-10-03 09:36:10 +00:00
|
|
|
transferrables.push(mergedAttributes.tops[0].buffer);
|
|
|
|
transferrables.push(mergedAttributes.tops[1].buffer);
|
|
|
|
|
2016-09-13 10:09:01 +00:00
|
|
|
transferrables.push(mergedOutlineAttributes.positions[0].buffer);
|
|
|
|
transferrables.push(mergedOutlineAttributes.positions[1].buffer);
|
|
|
|
|
|
|
|
transferrables.push(mergedOutlineAttributes.colors[0].buffer);
|
|
|
|
transferrables.push(mergedOutlineAttributes.colors[1].buffer);
|
|
|
|
|
2016-09-08 15:32:26 +00:00
|
|
|
var mergedProperties;
|
|
|
|
if (_properties) {
|
|
|
|
mergedProperties = Buffer.mergeUint8Arrays(properties);
|
|
|
|
|
|
|
|
transferrables.push(mergedProperties[0].buffer);
|
|
|
|
transferrables.push(mergedProperties[1].buffer);
|
|
|
|
}
|
|
|
|
|
|
|
|
var output = {
|
|
|
|
attributes: mergedAttributes,
|
2016-09-13 10:09:01 +00:00
|
|
|
outlineAttributes: mergedOutlineAttributes,
|
2016-09-08 15:32:26 +00:00
|
|
|
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 ProcessPolylines(polylinePromises, polylines, _properties) {
|
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
Promise.all(polylinePromises).then((results) => {
|
|
|
|
var transferrables = [];
|
|
|
|
|
|
|
|
var positions = [];
|
|
|
|
var colors = [];
|
|
|
|
|
|
|
|
var properties = [];
|
|
|
|
|
|
|
|
var flats = [];
|
|
|
|
var polyline;
|
|
|
|
|
|
|
|
var result;
|
|
|
|
for (var i = 0; i < results.length; i++) {
|
|
|
|
result = results[i];
|
|
|
|
|
|
|
|
polyline = polylines[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);
|
2016-09-01 14:50:51 +00:00
|
|
|
|
2016-09-08 15:32:26 +00:00
|
|
|
// 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);
|
|
|
|
colors.push(attributes.colors);
|
|
|
|
|
|
|
|
if (_properties) {
|
|
|
|
properties.push(Buffer.stringToUint8Array(JSON.stringify(polyline.properties)));
|
|
|
|
}
|
2016-09-01 14:50:51 +00:00
|
|
|
};
|
2016-09-08 15:32:26 +00:00
|
|
|
};
|
2016-09-01 14:50:51 +00:00
|
|
|
|
2016-09-08 15:32:26 +00:00
|
|
|
var mergedAttributes = {
|
|
|
|
positions: Buffer.mergeFloat32Arrays(positions),
|
|
|
|
colors: Buffer.mergeFloat32Arrays(colors)
|
|
|
|
};
|
2016-09-01 14:50:51 +00:00
|
|
|
|
2016-09-08 15:32:26 +00:00
|
|
|
transferrables.push(mergedAttributes.positions[0].buffer);
|
|
|
|
transferrables.push(mergedAttributes.positions[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
|
2016-09-01 14:50:51 +00:00
|
|
|
});
|
2016-09-08 15:32:26 +00:00
|
|
|
}).catch(reject);
|
2016-09-01 14:50:51 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2016-09-09 10:39:16 +00:00
|
|
|
// 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);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2016-09-01 14:50:51 +00:00
|
|
|
static ProcessGeoJSON(geojson, headers) {
|
|
|
|
if (typeof geojson === 'string') {
|
|
|
|
return GeoJSONWorkerLayer.RequestGeoJSON(geojson, headers);
|
|
|
|
} else {
|
|
|
|
return Promise.resolve(JSON.parse(Buffer.uint8ArrayToString(geojson)));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static RequestGeoJSON(path, headers) {
|
|
|
|
return reqwest({
|
|
|
|
url: path,
|
|
|
|
type: 'json',
|
|
|
|
crossOrigin: true,
|
|
|
|
headers: headers
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create and store mesh from buffer attributes
|
|
|
|
//
|
|
|
|
// Could make this an abstract method for each geometry layer
|
|
|
|
_setPolygonMesh(attributes, attributeLengths, style, flat) {
|
2016-09-08 13:02:55 +00:00
|
|
|
return PolygonLayer.SetMesh(attributes, attributeLengths, flat, style, this._options, this._world._environment._skybox);
|
2016-09-01 14:50:51 +00:00
|
|
|
}
|
|
|
|
|
2016-09-08 16:17:49 +00:00
|
|
|
_setPolylineMesh(attributes, attributeLengths, style, flat) {
|
|
|
|
return PolylineLayer.SetMesh(attributes, attributeLengths, flat, style, this._options);
|
|
|
|
}
|
|
|
|
|
2016-09-09 10:39:16 +00:00
|
|
|
_setPointMesh(attributes, attributeLengths, style, flat) {
|
|
|
|
return PointLayer.SetMesh(attributes, attributeLengths, flat, style, this._options, this._world._environment._skybox);
|
|
|
|
}
|
|
|
|
|
2016-09-01 14:50:51 +00:00
|
|
|
// Set up and re-emit interaction events
|
|
|
|
_addPicking(pickingId, properties) {
|
|
|
|
this._world.on('pick-click-' + pickingId, (pickingId, point2d, point3d, intersects) => {
|
|
|
|
this._world.emit('click', this, properties);
|
|
|
|
});
|
|
|
|
|
|
|
|
this._world.on('pick-hover-' + pickingId, (pickingId, point2d, point3d, intersects) => {
|
|
|
|
this._world.emit('hover', this, properties);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: Finish cleanup
|
|
|
|
destroy() {
|
|
|
|
// Run common destruction logic from parent
|
|
|
|
super.destroy();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export default GeoJSONWorkerLayer;
|
|
|
|
|
|
|
|
var noNew = function(geojson, options) {
|
|
|
|
return new GeoJSONWorkerLayer(geojson, options);
|
|
|
|
};
|
|
|
|
|
|
|
|
export {noNew as geoJSONWorkerLayer};
|