diff --git a/examples/geojson/main.js b/examples/geojson/main.js index a08130f..c4c9695 100644 --- a/examples/geojson/main.js +++ b/examples/geojson/main.js @@ -19,6 +19,8 @@ VIZI.geoJSONLayer('http://vector.mapzen.com/osm/roads,pois,buildings/14/4824/615 output: true, style: { color: '#ff0000', + outline: true, + outlineColor: '#580000', lineColor: '#0000ff', lineRenderOrder: 1, pointColor: '#00cc00' diff --git a/examples/web-workers-geojson/main.js b/examples/web-workers-geojson/main.js index 67d626e..a8e57f2 100644 --- a/examples/web-workers-geojson/main.js +++ b/examples/web-workers-geojson/main.js @@ -23,6 +23,8 @@ world.createWorkers(7).then(() => { output: true, style: { color: '#ff0000', + outline: true, + outlineColor: '#580000', lineColor: '#0000ff', lineRenderOrder: 1, pointColor: '#00cc00' diff --git a/src/layer/GeoJSONLayer.js b/src/layer/GeoJSONLayer.js index 5e78a37..8262c43 100644 --- a/src/layer/GeoJSONLayer.js +++ b/src/layer/GeoJSONLayer.js @@ -170,6 +170,7 @@ class GeoJSONLayer extends LayerGroup { // From here on we can assume that we want to merge the layers var polygonAttributes = []; + var polygonOutlineAttributes = []; var polygonAttributeLengths = { positions: 3, normals: 3, @@ -196,6 +197,11 @@ class GeoJSONLayer extends LayerGroup { if (layer instanceof PolygonLayer) { polygonAttributes.push(layer.getBufferAttributes()); + var outlineBufferAttributes = layer.getOutlineBufferAttributes(); + if (outlineBufferAttributes) { + polygonOutlineAttributes.push(outlineBufferAttributes); + } + if (polygonFlat && !layer.isFlat()) { polygonFlat = false; } @@ -228,10 +234,22 @@ class GeoJSONLayer extends LayerGroup { if (polygonAttributes.length > 0) { var mergedPolygonAttributes = Buffer.mergeAttributes(polygonAttributes); + + var mergedPolygonOutlineAttributes; + if (polygonOutlineAttributes.length > 0) { + mergedPolygonOutlineAttributes = Buffer.mergeAttributes(polygonOutlineAttributes); + } + this._setPolygonMesh(mergedPolygonAttributes, polygonAttributeLengths, polygonFlat).then((result) => { this._polygonMesh = result.mesh; this.add(this._polygonMesh); + if (mergedPolygonOutlineAttributes) { + this._setPolylineMesh(mergedPolygonOutlineAttributes, polylineAttributeLengths, true).then((result) => { + this.add(result.mesh); + }); + } + if (result.pickingMesh) { this._pickingMesh.add(result.pickingMesh); } diff --git a/src/layer/GeoJSONWorkerLayer.js b/src/layer/GeoJSONWorkerLayer.js index 03ed4c3..d947817 100644 --- a/src/layer/GeoJSONWorkerLayer.js +++ b/src/layer/GeoJSONWorkerLayer.js @@ -141,6 +141,14 @@ class GeoJSONWorkerLayer extends Layer { var splitNormals = Buffer.splitFloat32Array(results.attributes.normals); var splitColors = Buffer.splitFloat32Array(results.attributes.colors); + 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); @@ -149,6 +157,8 @@ class GeoJSONWorkerLayer extends Layer { var flats = results.flats; var objects = []; + var outlineObjects = []; + var obj; var pickingId; var pickingIds; @@ -160,6 +170,11 @@ class GeoJSONWorkerLayer extends Layer { colors: 3 }; + 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])); @@ -208,7 +223,20 @@ class GeoJSONWorkerLayer extends Layer { 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; @@ -224,6 +252,13 @@ class GeoJSONWorkerLayer extends Layer { 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); @@ -240,6 +275,17 @@ class GeoJSONWorkerLayer extends Layer { } }); } + + 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 @@ -683,6 +729,9 @@ class GeoJSONWorkerLayer extends Layer { var normals = []; var colors = []; + var outlinePositions = []; + var outlineColors = []; + var properties = []; var flats = []; @@ -713,6 +762,14 @@ class GeoJSONWorkerLayer extends Layer { properties.push(Buffer.stringToUint8Array(JSON.stringify(polygon.properties))); } }; + + var outlineAttributes; + for (var j = 0; j < result.outlineAttributes.length; j++) { + outlineAttributes = result.outlineAttributes[j]; + + outlinePositions.push(outlineAttributes.positions); + outlineColors.push(outlineAttributes.colors); + }; }; var mergedAttributes = { @@ -721,6 +778,11 @@ class GeoJSONWorkerLayer extends Layer { colors: Buffer.mergeFloat32Arrays(colors) }; + var mergedOutlineAttributes = { + positions: Buffer.mergeFloat32Arrays(outlinePositions), + colors: Buffer.mergeFloat32Arrays(outlineColors) + }; + transferrables.push(mergedAttributes.positions[0].buffer); transferrables.push(mergedAttributes.positions[1].buffer); @@ -730,6 +792,12 @@ class GeoJSONWorkerLayer extends Layer { transferrables.push(mergedAttributes.colors[0].buffer); transferrables.push(mergedAttributes.colors[1].buffer); + transferrables.push(mergedOutlineAttributes.positions[0].buffer); + transferrables.push(mergedOutlineAttributes.positions[1].buffer); + + transferrables.push(mergedOutlineAttributes.colors[0].buffer); + transferrables.push(mergedOutlineAttributes.colors[1].buffer); + var mergedProperties; if (_properties) { mergedProperties = Buffer.mergeUint8Arrays(properties); @@ -740,6 +808,7 @@ class GeoJSONWorkerLayer extends Layer { var output = { attributes: mergedAttributes, + outlineAttributes: mergedOutlineAttributes, flats: flats }; diff --git a/src/layer/geometry/PolygonLayer.js b/src/layer/geometry/PolygonLayer.js index b1d8548..00606a6 100644 --- a/src/layer/geometry/PolygonLayer.js +++ b/src/layer/geometry/PolygonLayer.js @@ -76,19 +76,24 @@ class PolygonLayer extends Layer { PolygonLayer.SetBufferAttributes(this._projectedCoordinates, this._options).then((result) => { this._bufferAttributes = Buffer.mergeAttributes(result.attributes); - this._flat = result.flat; - var attributeLengths = { - positions: 3, - normals: 3, - colors: 3 - }; - - if (this._options.interactive) { - attributeLengths.pickingIds = 1; + if (result.outlineAttributes.length > 0) { + this._outlineBufferAttributes = Buffer.mergeAttributes(result.outlineAttributes); } + this._flat = result.flat; + if (this.isOutput()) { + var attributeLengths = { + positions: 3, + normals: 3, + colors: 3 + }; + + if (this._options.interactive) { + attributeLengths.pickingIds = 1; + } + var style = this._options.style; // Set mesh if not merging elsewhere @@ -103,6 +108,7 @@ class PolygonLayer extends Layer { } result.attributes = null; + result.outlineAttributes = null; result = null; resolve(this); @@ -158,6 +164,8 @@ class PolygonLayer extends Layer { var flat = true; + var outlineAttributes = []; + // For each polygon var attributes = coordinates.map(_coordinates => { // Convert coordinates to earcut format @@ -230,6 +238,13 @@ class PolygonLayer extends Layer { facesCount: _faces.length }; + if (options.style.outline) { + var outlineColour = new THREE.Color(); + outlineColour.set(options.style.outlineColor || 0x000000); + + outlineAttributes.push(PolygonLayer.Set2DOutline(_coordinates, outlineColour)); + } + if (options.interactive && options.pickingId) { // Inject picking ID polygon.pickingId = options.pickingId; @@ -241,6 +256,7 @@ class PolygonLayer extends Layer { resolve({ attributes: attributes, + outlineAttributes: outlineAttributes, flat: flat }); }); @@ -250,6 +266,10 @@ class PolygonLayer extends Layer { return this._bufferAttributes; } + getOutlineBufferAttributes() { + return this._outlineBufferAttributes; + } + // Used by external components to clear some memory when the attributes // are no longer required to be stored in this layer // @@ -257,6 +277,64 @@ class PolygonLayer extends Layer { // using something like the GeoJSONLayer clearBufferAttributes() { this._bufferAttributes = null; + this._outlineBufferAttributes = null; + } + + // Threshold angle is currently in rads + static Set2DOutline(coordinates, colour) { + var _vertices = []; + + coordinates.forEach((ring) => { + var _ring = ring.map((coordinate) => { + return [coordinate.x, 0, coordinate.y]; + }); + + // Add in duplicate vertices for line segments to work + var verticeCount = _ring.length; + var first = true; + while (--verticeCount) { + if (first || verticeCount === 0) { + first = false; + continue; + } + + _ring.splice(verticeCount + 1, 0, _ring[verticeCount]); + } + + _vertices = _vertices.concat(_ring); + }); + + _colour = [colour.r, colour.g, colour.b]; + + var vertices = new Float32Array(_vertices.length * 3); + var colours = new Float32Array(_vertices.length * 3); + + var lastIndex = 0; + + for (var i = 0; i < _vertices.length; i++) { + var ax = _vertices[i][0]; + var ay = _vertices[i][1]; + var az = _vertices[i][2]; + + var c1 = _colour; + + vertices[lastIndex * 3 + 0] = ax; + vertices[lastIndex * 3 + 1] = ay; + vertices[lastIndex * 3 + 2] = az; + + colours[lastIndex * 3 + 0] = c1[0]; + colours[lastIndex * 3 + 1] = c1[1]; + colours[lastIndex * 3 + 2] = c1[2]; + + lastIndex++; + } + + var attributes = { + positions: vertices, + colors: colours + }; + + return attributes; } // Used by external components to clear some memory when the coordinates diff --git a/src/util/GeoJSON.js b/src/util/GeoJSON.js index a326c82..fd6e089 100644 --- a/src/util/GeoJSON.js +++ b/src/util/GeoJSON.js @@ -23,6 +23,8 @@ var shadow = new THREE.Color(0x666666); var GeoJSON = (function() { var defaultStyle = { color: '#ffffff', + outline: false, + outlineColor: '#000000', transparent: false, opacity: 1, blending: THREE.NormalBlending,