kopia lustrzana https://github.com/robhawkes/vizicities
Merge pull request #183 from UDST/feature/tile-display-bug
Initial fix for tile duplication bugfeature/threejs-update
commit
06de12be9d
|
@ -242,6 +242,8 @@ class World extends EventEmitter {
|
|||
}
|
||||
|
||||
addLayer(layer) {
|
||||
// Is is right to assume that there will always be some other layer
|
||||
// managing layers with output set to false?
|
||||
this._layers.push(layer);
|
||||
|
||||
if (layer.isOutput() && layer.isOutputToScene()) {
|
||||
|
@ -256,7 +258,11 @@ class World extends EventEmitter {
|
|||
if (layer._options.attribution) {
|
||||
this._addAttribution(layer._options.id, layer._options.attribution);
|
||||
}
|
||||
|
||||
// TODO: Consider moving this so it doesn't fire for layers that are
|
||||
// actually managed by a parent layer (eg. tiles)
|
||||
this.emit('layerAdded', layer);
|
||||
|
||||
resolve(this);
|
||||
}).catch(reject);
|
||||
});
|
||||
|
|
|
@ -38,6 +38,7 @@ class GeoJSONWorkerLayer extends Layer {
|
|||
|
||||
super(_options);
|
||||
|
||||
this._aborted = false;
|
||||
this._geojson = geojson;
|
||||
}
|
||||
|
||||
|
@ -80,6 +81,11 @@ class GeoJSONWorkerLayer extends Layer {
|
|||
}).catch(reject);
|
||||
} else if (typeof this._options.filter === 'function' || typeof this._options.onEachFeature === 'function') {
|
||||
GeoJSONWorkerLayer.RequestGeoJSON(geojson).then((res) => {
|
||||
// if (this._aborted) {
|
||||
// resolve();
|
||||
// return;
|
||||
// }
|
||||
|
||||
var fc = GeoJSON.collectFeatures(res, this._options.topojson);
|
||||
var features = fc.features;
|
||||
|
||||
|
@ -118,16 +124,198 @@ class GeoJSONWorkerLayer extends Layer {
|
|||
Worker.exec('GeoJSONWorkerLayer.Process', [geojson, topojson, headers, originPoint, style, interactive, pointGeometry], transferrables).then((results) => {
|
||||
console.timeEnd('Worker round trip');
|
||||
|
||||
// if (this._aborted) {
|
||||
// resolve();
|
||||
// return;
|
||||
// }
|
||||
|
||||
var processPromises = [];
|
||||
|
||||
if (results.polygons) {
|
||||
this._processPolygonResults(results.polygons);
|
||||
processPromises.push(this._processPolygonResults(results.polygons));
|
||||
}
|
||||
|
||||
if (results.polylines) {
|
||||
this._processPolylineResults(results.polylines);
|
||||
processPromises.push(this._processPolylineResults(results.polylines));
|
||||
}
|
||||
|
||||
if (results.points) {
|
||||
this._processPointResults(results.points);
|
||||
processPromises.push(this._processPointResults(results.points));
|
||||
}
|
||||
|
||||
if (processPromises.length > 0) {
|
||||
Promise.all(processPromises).then(() => {
|
||||
resolve();
|
||||
});
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// TODO: Dedupe with polyline method
|
||||
_processPolygonResults(results) {
|
||||
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);
|
||||
|
||||
var splitOutlinePositions;
|
||||
var splitOutlineColors;
|
||||
|
||||
if (results.outlineAttributes) {
|
||||
splitOutlinePositions = Buffer.splitFloat32Array(results.outlineAttributes.positions);
|
||||
splitOutlineColors = Buffer.splitFloat32Array(results.outlineAttributes.colors);
|
||||
}
|
||||
|
||||
var splitProperties;
|
||||
if (results.properties) {
|
||||
splitProperties = Buffer.splitUint8Array(results.properties);
|
||||
}
|
||||
|
||||
var flats = results.flats;
|
||||
|
||||
var objects = [];
|
||||
var outlineObjects = [];
|
||||
|
||||
var obj;
|
||||
var pickingId;
|
||||
var pickingIds;
|
||||
var properties;
|
||||
|
||||
var polygonAttributeLengths = {
|
||||
positions: 3,
|
||||
normals: 3,
|
||||
colors: 3,
|
||||
tops: 1
|
||||
};
|
||||
|
||||
var polygonOutlineAttributeLengths = {
|
||||
positions: 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],
|
||||
tops: splitTops[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;
|
||||
|
||||
polygonAttributeLengths.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;
|
||||
polygonAttributeLengths[key] = customAttribute.length;
|
||||
}
|
||||
}
|
||||
|
||||
objects.push(obj);
|
||||
}
|
||||
|
||||
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];
|
||||
|
||||
if (polygonFlat && !obj.flat) {
|
||||
polygonFlat = false;
|
||||
}
|
||||
|
||||
var bufferAttributes = Buffer.mergeAttributes(obj.attributes);
|
||||
polygonAttributes.push(bufferAttributes);
|
||||
};
|
||||
|
||||
for (var i = 0; i < outlineObjects.length; i++) {
|
||||
obj = outlineObjects[i];
|
||||
|
||||
var bufferAttributes = Buffer.mergeAttributes(obj.attributes);
|
||||
polygonOutlineAttributes.push(bufferAttributes);
|
||||
};
|
||||
|
||||
var outputPromises = [];
|
||||
|
||||
if (polygonAttributes.length > 0) {
|
||||
var mergedPolygonAttributes = Buffer.mergeAttributes(polygonAttributes);
|
||||
|
||||
// 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);
|
||||
|
||||
outputPromises.push(this._setPolygonMesh(mergedPolygonAttributes, polygonAttributeLengths, style, polygonFlat));
|
||||
}
|
||||
|
||||
if (polygonOutlineAttributes.length > 0) {
|
||||
var mergedPolygonOutlineAttributes = Buffer.mergeAttributes(polygonOutlineAttributes);
|
||||
|
||||
var style = (typeof this._options.style === 'function') ? this._options.style(objects[0]) : this._options.style;
|
||||
style = extend({}, GeoJSON.defaultStyle, style);
|
||||
|
||||
outputPromises.push(this._setPolylineMesh(mergedPolygonOutlineAttributes, polygonOutlineAttributeLengths, style, true));
|
||||
}
|
||||
|
||||
Promise.all(outputPromises).then((results) => {
|
||||
var [polygonResult, outlineResult] = results;
|
||||
|
||||
if (polygonResult) {
|
||||
this._polygonMesh = polygonResult.mesh;
|
||||
this.add(this._polygonMesh);
|
||||
|
||||
if (polygonResult.pickingMesh) {
|
||||
this._pickingMesh.add(polygonResult.pickingMesh);
|
||||
}
|
||||
}
|
||||
|
||||
if (outlineResult) {
|
||||
this.add(outlineResult.mesh);
|
||||
}
|
||||
|
||||
resolve();
|
||||
|
@ -135,370 +323,224 @@ class GeoJSONWorkerLayer extends Layer {
|
|||
});
|
||||
}
|
||||
|
||||
// TODO: Dedupe with polyline method
|
||||
_processPolygonResults(results) {
|
||||
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);
|
||||
|
||||
var splitOutlinePositions;
|
||||
var splitOutlineColors;
|
||||
|
||||
if (results.outlineAttributes) {
|
||||
splitOutlinePositions = Buffer.splitFloat32Array(results.outlineAttributes.positions);
|
||||
splitOutlineColors = Buffer.splitFloat32Array(results.outlineAttributes.colors);
|
||||
}
|
||||
|
||||
var splitProperties;
|
||||
if (results.properties) {
|
||||
splitProperties = Buffer.splitUint8Array(results.properties);
|
||||
}
|
||||
|
||||
var flats = results.flats;
|
||||
|
||||
var objects = [];
|
||||
var outlineObjects = [];
|
||||
|
||||
var obj;
|
||||
var pickingId;
|
||||
var pickingIds;
|
||||
var properties;
|
||||
|
||||
var polygonAttributeLengths = {
|
||||
positions: 3,
|
||||
normals: 3,
|
||||
colors: 3,
|
||||
tops: 1
|
||||
};
|
||||
|
||||
var polygonOutlineAttributeLengths = {
|
||||
positions: 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],
|
||||
tops: splitTops[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;
|
||||
|
||||
polygonAttributeLengths.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;
|
||||
polygonAttributeLengths[key] = customAttribute.length;
|
||||
}
|
||||
}
|
||||
|
||||
objects.push(obj);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
var obj;
|
||||
for (var i = 0; i < objects.length; i++) {
|
||||
obj = objects[i];
|
||||
|
||||
if (polygonFlat && !obj.flat) {
|
||||
polygonFlat = false;
|
||||
}
|
||||
|
||||
var bufferAttributes = Buffer.mergeAttributes(obj.attributes);
|
||||
polygonAttributes.push(bufferAttributes);
|
||||
};
|
||||
|
||||
for (var i = 0; i < outlineObjects.length; i++) {
|
||||
obj = outlineObjects[i];
|
||||
|
||||
var bufferAttributes = Buffer.mergeAttributes(obj.attributes);
|
||||
polygonOutlineAttributes.push(bufferAttributes);
|
||||
};
|
||||
|
||||
if (polygonAttributes.length > 0) {
|
||||
var mergedPolygonAttributes = Buffer.mergeAttributes(polygonAttributes);
|
||||
|
||||
// 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._setPolygonMesh(mergedPolygonAttributes, polygonAttributeLengths, style, polygonFlat).then((result) => {
|
||||
this._polygonMesh = result.mesh;
|
||||
this.add(this._polygonMesh);
|
||||
|
||||
if (result.pickingMesh) {
|
||||
this._pickingMesh.add(result.pickingMesh);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (polygonOutlineAttributes.length > 0) {
|
||||
var mergedPolygonOutlineAttributes = Buffer.mergeAttributes(polygonOutlineAttributes);
|
||||
|
||||
var style = (typeof this._options.style === 'function') ? this._options.style(objects[0]) : this._options.style;
|
||||
style = extend({}, GeoJSON.defaultStyle, style);
|
||||
|
||||
this._setPolylineMesh(mergedPolygonOutlineAttributes, polygonOutlineAttributeLengths, style, true).then((result) => {
|
||||
this.add(result.mesh);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Dedupe with polygon method
|
||||
_processPolylineResults(results) {
|
||||
var splitPositions = Buffer.splitFloat32Array(results.attributes.positions);
|
||||
var splitColors = Buffer.splitFloat32Array(results.attributes.colors);
|
||||
return new Promise((resolve, reject) => {
|
||||
var splitPositions = Buffer.splitFloat32Array(results.attributes.positions);
|
||||
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 polylineAttributeLengths = {
|
||||
positions: 3,
|
||||
colors: 3
|
||||
};
|
||||
|
||||
for (var i = 0; i < splitPositions.length; i++) {
|
||||
if (splitProperties && splitProperties[i]) {
|
||||
properties = JSON.parse(Buffer.uint8ArrayToString(splitProperties[i]));
|
||||
} else {
|
||||
properties = {};
|
||||
var splitProperties;
|
||||
if (results.properties) {
|
||||
splitProperties = Buffer.splitUint8Array(results.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],
|
||||
colors: splitColors[i]
|
||||
}],
|
||||
properties: properties,
|
||||
flat: flats[i]
|
||||
var flats = results.flats;
|
||||
|
||||
var objects = [];
|
||||
var obj;
|
||||
var pickingId;
|
||||
var pickingIds;
|
||||
var properties;
|
||||
|
||||
var polylineAttributeLengths = {
|
||||
positions: 3,
|
||||
colors: 3
|
||||
};
|
||||
|
||||
// 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;
|
||||
|
||||
polylineAttributeLengths.pickingIds = 1;
|
||||
|
||||
this._addPicking(pickingId, properties);
|
||||
}
|
||||
|
||||
// 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;
|
||||
for (var i = 0; i < splitPositions.length; i++) {
|
||||
if (splitProperties && splitProperties[i]) {
|
||||
properties = JSON.parse(Buffer.uint8ArrayToString(splitProperties[i]));
|
||||
} else {
|
||||
properties = {};
|
||||
}
|
||||
}
|
||||
|
||||
objects.push(obj);
|
||||
}
|
||||
// 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]
|
||||
};
|
||||
|
||||
var polylineAttributes = [];
|
||||
// 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();
|
||||
|
||||
var polylineFlat = true;
|
||||
pickingIds = new Float32Array(splitPositions[i].length / 3);
|
||||
pickingIds.fill(pickingId);
|
||||
|
||||
var obj;
|
||||
for (var i = 0; i < objects.length; i++) {
|
||||
obj = objects[i];
|
||||
obj.attributes[0].pickingIds = pickingIds;
|
||||
|
||||
if (polylineFlat && !obj.flat) {
|
||||
polylineFlat = false;
|
||||
}
|
||||
polylineAttributeLengths.pickingIds = 1;
|
||||
|
||||
var bufferAttributes = Buffer.mergeAttributes(obj.attributes);
|
||||
polylineAttributes.push(bufferAttributes);
|
||||
};
|
||||
|
||||
if (polylineAttributes.length > 0) {
|
||||
var mergedPolylineAttributes = Buffer.mergeAttributes(polylineAttributes);
|
||||
|
||||
// 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._setPolylineMesh(mergedPolylineAttributes, polylineAttributeLengths, style, polylineFlat).then((result) => {
|
||||
this._polylineMesh = result.mesh;
|
||||
this.add(this._polylineMesh);
|
||||
|
||||
if (result.pickingMesh) {
|
||||
this._pickingMesh.add(result.pickingMesh);
|
||||
this._addPicking(pickingId, properties);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
|
||||
objects.push(obj);
|
||||
}
|
||||
|
||||
var polylineAttributes = [];
|
||||
|
||||
var polylineFlat = true;
|
||||
|
||||
for (var i = 0; i < objects.length; i++) {
|
||||
obj = objects[i];
|
||||
|
||||
if (polylineFlat && !obj.flat) {
|
||||
polylineFlat = false;
|
||||
}
|
||||
|
||||
var bufferAttributes = Buffer.mergeAttributes(obj.attributes);
|
||||
polylineAttributes.push(bufferAttributes);
|
||||
};
|
||||
|
||||
if (polylineAttributes.length > 0) {
|
||||
var mergedPolylineAttributes = Buffer.mergeAttributes(polylineAttributes);
|
||||
|
||||
// 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._setPolylineMesh(mergedPolylineAttributes, polylineAttributeLengths, style, polylineFlat).then((result) => {
|
||||
this._polylineMesh = result.mesh;
|
||||
this.add(this._polylineMesh);
|
||||
|
||||
if (result.pickingMesh) {
|
||||
this._pickingMesh.add(result.pickingMesh);
|
||||
}
|
||||
|
||||
resolve();
|
||||
});
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
_processPointResults(results) {
|
||||
var splitPositions = Buffer.splitFloat32Array(results.attributes.positions);
|
||||
var splitNormals = Buffer.splitFloat32Array(results.attributes.normals);
|
||||
var splitColors = Buffer.splitFloat32Array(results.attributes.colors);
|
||||
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 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 = {};
|
||||
var splitProperties;
|
||||
if (results.properties) {
|
||||
splitProperties = Buffer.splitUint8Array(results.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]
|
||||
var flats = results.flats;
|
||||
|
||||
var objects = [];
|
||||
var obj;
|
||||
var pickingId;
|
||||
var pickingIds;
|
||||
var properties;
|
||||
|
||||
var pointAttributeLengths = {
|
||||
positions: 3,
|
||||
normals: 3,
|
||||
colors: 3
|
||||
};
|
||||
|
||||
// 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;
|
||||
for (var i = 0; i < splitPositions.length; i++) {
|
||||
if (splitProperties && splitProperties[i]) {
|
||||
properties = JSON.parse(Buffer.uint8ArrayToString(splitProperties[i]));
|
||||
} else {
|
||||
properties = {};
|
||||
}
|
||||
}
|
||||
|
||||
objects.push(obj);
|
||||
}
|
||||
// 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]
|
||||
};
|
||||
|
||||
var pointAttributes = [];
|
||||
// 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();
|
||||
|
||||
var pointFlat = true;
|
||||
pickingIds = new Float32Array(splitPositions[i].length / 3);
|
||||
pickingIds.fill(pickingId);
|
||||
|
||||
var obj;
|
||||
for (var i = 0; i < objects.length; i++) {
|
||||
obj = objects[i];
|
||||
obj.attributes[0].pickingIds = pickingIds;
|
||||
|
||||
if (pointFlat && !obj.flat) {
|
||||
pointFlat = false;
|
||||
}
|
||||
pointAttributeLengths.pickingIds = 1;
|
||||
|
||||
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);
|
||||
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;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
resolve();
|
||||
});
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// TODO: At some point this needs to return all the features to the main thread
|
||||
|
|
|
@ -89,11 +89,10 @@ class GeoJSONTile extends Tile {
|
|||
setTimeout(() => {
|
||||
if (!this._mesh) {
|
||||
this._mesh = this._createMesh();
|
||||
|
||||
// this._shadowCanvas = this._createShadowCanvas();
|
||||
|
||||
this._requestTile();
|
||||
}
|
||||
|
||||
this._requestTile();
|
||||
}, 0);
|
||||
}
|
||||
|
||||
|
@ -233,6 +232,8 @@ class GeoJSONTile extends Tile {
|
|||
|
||||
var url = this._getTileURL(urlParams);
|
||||
|
||||
this._aborted = false;
|
||||
|
||||
if (!this._options.workers) {
|
||||
this._request = reqwest({
|
||||
url: url,
|
||||
|
@ -244,8 +245,6 @@ class GeoJSONTile extends Tile {
|
|||
this._request = null;
|
||||
this._processTileData(res);
|
||||
}).catch(err => {
|
||||
console.error(err);
|
||||
|
||||
// Clear request reference
|
||||
this._request = null;
|
||||
});
|
||||
|
@ -255,13 +254,37 @@ class GeoJSONTile extends Tile {
|
|||
}
|
||||
|
||||
_processTileData(data) {
|
||||
console.time(this._tile);
|
||||
// console.time(this._tile);
|
||||
|
||||
var GeoJSONClass = (!this._options.workers) ? GeoJSONLayer : GeoJSONWorkerLayer;
|
||||
|
||||
// Using this creates a huge amount of memory due to the quantity of tiles
|
||||
this._geojsonLayer = GeoJSONClass(data, this._options);
|
||||
this._geojsonLayer.addTo(this._world).then(() => {
|
||||
// TODO: This never seems to be called on worker layers. Find out why.
|
||||
if (this.isAborted()) {
|
||||
// this._geojsonLayer._aborted = true;
|
||||
// this._geojsonLayer = null;
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: This is a hack to stop old tile meshes hanging around. Fix or
|
||||
// move to somewhere more robust.
|
||||
//
|
||||
// Could potentially just overwrite mesh on first index each time
|
||||
//
|
||||
// This makes some worker tiles to not appear properly – showing the
|
||||
// points mesh but not the polygon mesh, etc.
|
||||
//
|
||||
// Only do this for non-worker layers for now as it seems to cause issues
|
||||
// with worker tiles showing for a moment and then disappearing forever
|
||||
if (!this._options.workers) {
|
||||
this.destroyMesh(this._mesh);
|
||||
}
|
||||
|
||||
// TOSO: Work out if the picking mesh needs destroying here
|
||||
// this.destroyMesh(this._pickingMesh);
|
||||
|
||||
this._mesh.add(this._geojsonLayer._object3D);
|
||||
this._pickingMesh = this._geojsonLayer._pickingMesh;
|
||||
|
||||
|
@ -325,16 +348,20 @@ class GeoJSONTile extends Tile {
|
|||
// this._mesh.add(mesh);
|
||||
|
||||
this._ready = true;
|
||||
console.timeEnd(this._tile);
|
||||
// console.timeEnd(this._tile);
|
||||
});
|
||||
}
|
||||
|
||||
_abortRequest() {
|
||||
if (!this._request) {
|
||||
if ((!this._request && !this._options.workers) || this._ready) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._request.abort();
|
||||
this._aborted = true;
|
||||
|
||||
if (this._request) {
|
||||
this._request.abort();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -108,7 +108,10 @@ class GeoJSONTileLayer extends TileLayer {
|
|||
}
|
||||
|
||||
_createTile(quadcode, layer) {
|
||||
var newOptions = extend({}, this.defaults, this._options);
|
||||
var newOptions = extend({}, this.defaults, this._options, {
|
||||
outputToScene: false
|
||||
});
|
||||
|
||||
delete newOptions.attribution;
|
||||
|
||||
return new GeoJSONTile(quadcode, this._path, layer, newOptions);
|
||||
|
|
|
@ -15,8 +15,9 @@ class ImageTile extends Tile {
|
|||
setTimeout(() => {
|
||||
if (!this._mesh) {
|
||||
this._mesh = this._createMesh();
|
||||
this._requestTile();
|
||||
}
|
||||
|
||||
this._requestTile();
|
||||
}, 0);
|
||||
}
|
||||
|
||||
|
@ -131,7 +132,13 @@ class ImageTile extends Tile {
|
|||
|
||||
var image = document.createElement('img');
|
||||
|
||||
this._aborted = false;
|
||||
|
||||
image.addEventListener('load', event => {
|
||||
if (this.isAborted()) {
|
||||
return;
|
||||
}
|
||||
|
||||
var texture = new THREE.Texture();
|
||||
|
||||
texture.image = image;
|
||||
|
@ -172,10 +179,12 @@ class ImageTile extends Tile {
|
|||
}
|
||||
|
||||
_abortRequest() {
|
||||
if (!this._image) {
|
||||
if (!this._image || this._ready) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._aborted = true;
|
||||
|
||||
this._image.src = '';
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ class Tile {
|
|||
this._path = path;
|
||||
|
||||
this._ready = false;
|
||||
this._aborted = false;
|
||||
|
||||
this._tile = this._quadcodeToTile(quadcode);
|
||||
|
||||
|
@ -46,6 +47,10 @@ class Tile {
|
|||
return this._ready;
|
||||
}
|
||||
|
||||
isAborted() {
|
||||
return this._aborted;
|
||||
}
|
||||
|
||||
// Request data for the tile
|
||||
requestTileAsync() {}
|
||||
|
||||
|
@ -78,6 +83,8 @@ class Tile {
|
|||
// Ensure that this leaves no trace of the tile – no textures, no meshes,
|
||||
// nothing in memory or the GPU
|
||||
destroy() {
|
||||
// console.log('Destroying tile', this._quadcode);
|
||||
|
||||
// Delete reference to layer and world
|
||||
this._layer = null;
|
||||
this._world = null;
|
||||
|
@ -88,35 +95,42 @@ class Tile {
|
|||
this._center = null;
|
||||
|
||||
// Done if no mesh
|
||||
if (!this._mesh) {
|
||||
if (!this._mesh && !this._pickingMesh) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this._mesh.children) {
|
||||
// Dispose of mesh and materials
|
||||
this._mesh.children.forEach(child => {
|
||||
child.geometry.dispose();
|
||||
child.geometry = null;
|
||||
this.destroyMesh(this._mesh);
|
||||
this.destroyMesh(this._pickingMesh);
|
||||
|
||||
if (child.material.map) {
|
||||
child.material.map.dispose();
|
||||
child.material.map = null;
|
||||
}
|
||||
this._mesh = null;
|
||||
this._pickingMesh = null;
|
||||
}
|
||||
|
||||
child.material.dispose();
|
||||
child.material = null;
|
||||
});
|
||||
} else {
|
||||
this._mesh.geometry.dispose();
|
||||
this._mesh.geometry = null;
|
||||
|
||||
if (this._mesh.material.map) {
|
||||
this._mesh.material.map.dispose();
|
||||
this._mesh.material.map = null;
|
||||
destroyMesh(mesh, dispose = true) {
|
||||
if (mesh) {
|
||||
if (mesh.children) {
|
||||
mesh.children.forEach((child) => {
|
||||
mesh.remove(child);
|
||||
this.destroyMesh(child);
|
||||
});
|
||||
}
|
||||
|
||||
this._mesh.material.dispose();
|
||||
this._mesh.material = null;
|
||||
if (dispose) {
|
||||
if (mesh.geometry) {
|
||||
mesh.geometry.dispose();
|
||||
mesh.geometry = null;
|
||||
}
|
||||
|
||||
if (mesh.material) {
|
||||
if (mesh.material.map) {
|
||||
mesh.material.map.dispose();
|
||||
mesh.material.map = null;
|
||||
}
|
||||
|
||||
mesh.material.dispose();
|
||||
mesh.material = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -168,7 +168,7 @@ class TileLayer extends Layer {
|
|||
});
|
||||
|
||||
// 5. Filter the tiles remaining in the check list
|
||||
this._tileList = checkList.filter((tile, index) => {
|
||||
var tileList = checkList.filter((tile, index) => {
|
||||
// Skip tile if it's not in the current view frustum
|
||||
if (!this._tileInFrustum(tile)) {
|
||||
return false;
|
||||
|
@ -189,7 +189,7 @@ class TileLayer extends Layer {
|
|||
//
|
||||
// If yes, continue
|
||||
// If no, generate tile mesh, request texture and skip
|
||||
if (!tile.getMesh()) {
|
||||
if (!tile.getMesh() || tile.isAborted()) {
|
||||
tile.requestTileAsync();
|
||||
}
|
||||
|
||||
|
@ -207,6 +207,17 @@ class TileLayer extends Layer {
|
|||
// this._tiles.add(tile.getMesh());
|
||||
});
|
||||
|
||||
// Get list of tiles that were in the previous update but not the
|
||||
// current one (for aborting requests, etc)
|
||||
var missingTiles = this._tileList.filter((item) => {
|
||||
return !tileList.includes(item);
|
||||
});
|
||||
|
||||
// Abort tiles that are no longer in view
|
||||
missingTiles.forEach((tile) => tile._abortRequest());
|
||||
|
||||
this._tileList = tileList;
|
||||
|
||||
// console.log(performance.now() - start);
|
||||
}
|
||||
|
||||
|
|
Ładowanie…
Reference in New Issue