Added GeoJSONLayer with example and tidied up some other areas

master
Robin Hawkes 2016-02-28 20:29:52 +00:00
rodzic a04a25d803
commit 282fc3a58b
15 zmienionych plików z 892 dodań i 74 usunięć

Wyświetl plik

@ -8,4 +8,4 @@
"browser": true,
"node": true
}
}
}

510
dist/vizicities.js vendored
Wyświetl plik

@ -84,6 +84,10 @@ return /******/ (function(modules) { // webpackBootstrap
var _layerTileTopoJSONTileLayer2 = _interopRequireDefault(_layerTileTopoJSONTileLayer);
var _layerGeoJSONLayer = __webpack_require__(65);
var _layerGeoJSONLayer2 = _interopRequireDefault(_layerGeoJSONLayer);
var _geoPoint = __webpack_require__(11);
var _geoPoint2 = _interopRequireDefault(_geoPoint);
@ -102,6 +106,7 @@ return /******/ (function(modules) { // webpackBootstrap
ImageTileLayer: _layerTileImageTileLayer2['default'],
GeoJSONTileLayer: _layerTileGeoJSONTileLayer2['default'],
TopoJSONTileLayer: _layerTileTopoJSONTileLayer2['default'],
GeoJSONLayer: _layerGeoJSONLayer2['default'],
Point: _geoPoint2['default'],
LatLon: _geoLatLon2['default']
};
@ -151,6 +156,8 @@ return /******/ (function(modules) { // webpackBootstrap
var _engineEngine2 = _interopRequireDefault(_engineEngine);
// TODO: Make sure nothing is left behind in the heap after calling destroy()
// Pretty much any event someone using ViziCities would need will be emitted or
// proxied by World (eg. render events, etc)
@ -167,7 +174,7 @@ return /******/ (function(modules) { // webpackBootstrap
skybox: false
};
this.options = (0, _lodashAssign2['default'])(defaults, options);
this.options = (0, _lodashAssign2['default'])({}, defaults, options);
this._layers = [];
this._controls = [];
@ -7186,6 +7193,8 @@ return /******/ (function(modules) { // webpackBootstrap
var _Skybox2 = _interopRequireDefault(_Skybox);
// TODO: Make sure nothing is left behind in the heap after calling destroy()
var EnvironmentLayer = (function (_Layer) {
_inherits(EnvironmentLayer, _Layer);
@ -7345,6 +7354,8 @@ return /******/ (function(modules) { // webpackBootstrap
var _engineScene2 = _interopRequireDefault(_engineScene);
// TODO: Make sure nothing is left behind in the heap after calling destroy()
var Layer = (function (_EventEmitter) {
_inherits(Layer, _EventEmitter);
@ -7457,6 +7468,8 @@ return /******/ (function(modules) { // webpackBootstrap
var _lodashThrottle2 = _interopRequireDefault(_lodashThrottle);
// TODO: Make sure nothing is left behind in the heap after calling destroy()
var cubemap = {
vertexShader: ['varying vec3 vPosition;', 'void main() {', 'vPosition = position;', 'gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );', '}'].join('\n'),
@ -8207,6 +8220,8 @@ return /******/ (function(modules) { // webpackBootstrap
var _lodashAssign2 = _interopRequireDefault(_lodashAssign);
// TODO: Make sure nothing is left behind in the heap after calling destroy()
// DONE: Find a way to avoid the flashing caused by the gap between old tiles
// being removed and the new tiles being ready for display
//
@ -8266,7 +8281,7 @@ return /******/ (function(modules) { // webpackBootstrap
distance: 40000
};
options = (0, _lodashAssign2['default'])(defaults, options);
options = (0, _lodashAssign2['default'])({}, defaults, options);
_get(Object.getPrototypeOf(ImageTileLayer.prototype), 'constructor', this).call(this, options);
@ -8418,6 +8433,8 @@ return /******/ (function(modules) { // webpackBootstrap
var _three2 = _interopRequireDefault(_three);
// TODO: Make sure nothing is left behind in the heap after calling destroy()
// TODO: Consider keeping a single TileLayer / LOD instance running by default
// that keeps a standard LOD grid for other layers to utilise, rather than
// having to create their own, unique LOD grid and duplicate calculations when
@ -8473,7 +8490,7 @@ return /******/ (function(modules) { // webpackBootstrap
maxLOD: 18
};
this._options = (0, _lodashAssign2['default'])(defaults, options);
this._options = (0, _lodashAssign2['default'])({}, defaults, options);
this._tileCache = (0, _TileCache2['default'])(this._options.maxCache, function (tile) {
_this._destroyTile(tile);
@ -8781,6 +8798,8 @@ return /******/ (function(modules) { // webpackBootstrap
var _lruCache2 = _interopRequireDefault(_lruCache);
// TODO: Make sure nothing is left behind in the heap after calling destroy()
// This process is based on a similar approach taken by OpenWebGlobe
// See: https://github.com/OpenWebGlobe/WebViewer/blob/master/source/core/globecache.js
@ -10577,6 +10596,8 @@ return /******/ (function(modules) { // webpackBootstrap
var _three2 = _interopRequireDefault(_three);
// TODO: Make sure nothing is left behind in the heap after calling destroy()
var ImageTile = (function (_Tile) {
_inherits(ImageTile, _Tile);
@ -10807,6 +10828,8 @@ return /******/ (function(modules) { // webpackBootstrap
var _three2 = _interopRequireDefault(_three);
// TODO: Make sure nothing is left behind in the heap after calling destroy()
// Manages a single tile and its layers
var r2d = 180 / Math.PI;
@ -11221,6 +11244,8 @@ return /******/ (function(modules) { // webpackBootstrap
var _three2 = _interopRequireDefault(_three);
// TODO: Make sure nothing is left behind in the heap after calling destroy()
// TODO: Consider pausing per-frame output during movement so there's little to
// no jank caused by previous tiles still processing
@ -11255,7 +11280,7 @@ return /******/ (function(modules) { // webpackBootstrap
distance: 2000
};
options = (0, _lodashAssign2['default'])(defaults, options);
options = (0, _lodashAssign2['default'])({}, defaults, options);
_get(Object.getPrototypeOf(GeoJSONTileLayer.prototype), 'constructor', this).call(this, options);
@ -11418,6 +11443,8 @@ return /******/ (function(modules) { // webpackBootstrap
var _utilBuffer2 = _interopRequireDefault(_utilBuffer);
// TODO: Make sure nothing is left behind in the heap after calling destroy()
// TODO: Perform tile request and processing in a Web Worker
//
// Use Operative (https://github.com/padolsey/operative)
@ -11454,15 +11481,7 @@ return /******/ (function(modules) { // webpackBootstrap
_get(Object.getPrototypeOf(GeoJSONTile.prototype), 'constructor', this).call(this, quadcode, path, layer);
this._defaultStyle = {
color: '#ffffff',
height: 0,
lineOpacity: 1,
lineTransparent: false,
lineColor: '#ffffff',
lineWidth: 1,
lineBlending: _three2['default'].NormalBlending
};
this._defaultStyle = _utilGeoJSON2['default'].defaultStyle;
var defaults = {
topojson: false,
@ -11470,7 +11489,13 @@ return /******/ (function(modules) { // webpackBootstrap
style: this._defaultStyle
};
this._options = (0, _lodashAssign2['default'])(defaults, options);
this._options = (0, _lodashAssign2['default'])({}, defaults, options);
if (typeof options.style === 'function') {
this._options.style = options.style;
} else {
this._options.style = (0, _lodashAssign2['default'])({}, defaults.style, options.style);
}
}
// Initialise without requiring new keyword
@ -11721,7 +11746,7 @@ return /******/ (function(modules) { // webpackBootstrap
// Get style object, if provided
if (typeof _this3._options.style === 'function') {
style = (0, _lodashAssign2['default'])(_this3._defaultStyle, _this3._options.style(feature));
style = (0, _lodashAssign2['default'])({}, _this3._defaultStyle, _this3._options.style(feature));
}
var coordinates = feature.geometry.coordinates;
@ -11736,7 +11761,13 @@ return /******/ (function(modules) { // webpackBootstrap
return [point.x, point.y];
});
var linestringAttributes = _utilGeoJSON2['default'].lineStringAttributes(coordinates, colour);
var height = 0;
if (style.lineHeight) {
height = _this3._world.metresToWorld(style.lineHeight, _this3._pointScale);
}
var linestringAttributes = _utilGeoJSON2['default'].lineStringAttributes(coordinates, colour, height);
lines.vertices.push(linestringAttributes.vertices);
lines.colours.push(linestringAttributes.colours);
@ -11754,7 +11785,13 @@ return /******/ (function(modules) { // webpackBootstrap
});
});
var multiLinestringAttributes = _utilGeoJSON2['default'].multiLineStringAttributes(coordinates, colour);
var height = 0;
if (style.lineHeight) {
height = _this3._world.metresToWorld(style.lineHeight, _this3._pointScale);
}
var multiLinestringAttributes = _utilGeoJSON2['default'].multiLineStringAttributes(coordinates, colour, height);
lines.vertices.push(multiLinestringAttributes.vertices);
lines.colours.push(multiLinestringAttributes.colours);
@ -11843,11 +11880,15 @@ return /******/ (function(modules) { // webpackBootstrap
//
// this._mesh.add(mesh);
var geometry;
var material;
var mesh;
// Output lines
if (lines.vertices.length > 0) {
var geometry = _utilBuffer2['default'].createLineGeometry(lines, offset);
geometry = _utilBuffer2['default'].createLineGeometry(lines, offset);
var material = new _three2['default'].LineBasicMaterial({
material = new _three2['default'].LineBasicMaterial({
vertexColors: _three2['default'].VertexColors,
linewidth: style.lineWidth,
transparent: style.lineTransparent,
@ -11855,9 +11896,10 @@ return /******/ (function(modules) { // webpackBootstrap
blending: style.lineBlending
});
var mesh = new _three2['default'].LineSegments(geometry, material);
mesh = new _three2['default'].LineSegments(geometry, material);
if (style.lineRenderOrder) {
if (style.lineRenderOrder !== undefined) {
material.depthWrite = false;
mesh.renderOrder = style.lineRenderOrder;
}
@ -11869,9 +11911,8 @@ return /******/ (function(modules) { // webpackBootstrap
// Output polygons
if (polygons.facesCount > 0) {
var geometry = _utilBuffer2['default'].createGeometry(polygons, offset);
geometry = _utilBuffer2['default'].createGeometry(polygons, offset);
var material;
if (!this._world._environment._skybox) {
material = new _three2['default'].MeshPhongMaterial({
vertexColors: _three2['default'].VertexColors,
@ -11888,13 +11929,13 @@ return /******/ (function(modules) { // webpackBootstrap
material.envMap = this._world._environment._skybox.getRenderTarget();
}
var mesh = new _three2['default'].Mesh(geometry, material);
mesh = new _three2['default'].Mesh(geometry, material);
mesh.castShadow = true;
mesh.receiveShadow = true;
if (polygons.allFlat) {
// This is only useful for flat objects
material.depthWrite = false;
mesh.renderOrder = 1;
}
@ -12602,11 +12643,24 @@ return /******/ (function(modules) { // webpackBootstrap
var _extrudePolygon2 = _interopRequireDefault(_extrudePolygon);
// TODO: Make it so height can be per-coordinate for linestrings so you can do
// things like offsetting GPS points based on elevation at each point
// Light and dark colours used for poor-mans AO gradient on object sides
var light = new _three2['default'].Color(0xffffff);
var shadow = new _three2['default'].Color(0x666666);
var GeoJSON = (function () {
var defaultStyle = {
color: '#ffffff',
height: 0,
lineOpacity: 1,
lineTransparent: false,
lineColor: '#ffffff',
lineWidth: 1,
lineBlending: _three2['default'].NormalBlending
};
// Attempts to merge together multiple GeoJSON Features or FeatureCollections
// into a single FeatureCollection
var mergeFeatures = function mergeFeatures(data, _topojson) {
@ -12637,13 +12691,15 @@ return /******/ (function(modules) { // webpackBootstrap
}
return (0, _geojsonMerge2['default'])(collections);
} else {
} else if (Array.isArray(data)) {
return (0, _geojsonMerge2['default'])(data);
} else {
return data;
}
}
};
var lineStringAttributes = function lineStringAttributes(coordinates, colour) {
var lineStringAttributes = function lineStringAttributes(coordinates, colour, height) {
var _coords = [];
var _colours = [];
@ -12656,12 +12712,12 @@ return /******/ (function(modules) { // webpackBootstrap
coordinates.forEach(function (coordinate, index) {
// TODO: Don't hardcode y-value
_colours.push([colour.r, colour.g, colour.b]);
_coords.push([coordinate[0], 0, coordinate[1]]);
_coords.push([coordinate[0], height, coordinate[1]]);
nextCoord = coordinates[index + 1] ? coordinates[index + 1] : coordinate;
_colours.push([colour.r, colour.g, colour.b]);
_coords.push([nextCoord[0], 0, nextCoord[1]]);
_coords.push([nextCoord[0], height, nextCoord[1]]);
});
return {
@ -12670,13 +12726,13 @@ return /******/ (function(modules) { // webpackBootstrap
};
};
var multiLineStringAttributes = function multiLineStringAttributes(coordinates, colour) {
var multiLineStringAttributes = function multiLineStringAttributes(coordinates, colour, height) {
var _coords = [];
var _colours = [];
var result;
coordinates.forEach(function (coordinate) {
result = lineStringAttributes(coordinate, colour);
result = lineStringAttributes(coordinate, colour, height);
result.vertices.forEach(function (coord) {
_coords.push(coord);
@ -12803,6 +12859,7 @@ return /******/ (function(modules) { // webpackBootstrap
};
return {
defaultStyle: defaultStyle,
mergeFeatures: mergeFeatures,
lineStringAttributes: lineStringAttributes,
multiLineStringAttributes: multiLineStringAttributes,
@ -14049,7 +14106,7 @@ return /******/ (function(modules) { // webpackBootstrap
closed: true
};
var options = (0, _lodashAssign2['default'])(defaults, _options);
var options = (0, _lodashAssign2['default'])({}, defaults, _options);
var n = points.length;
var positions;
@ -14333,7 +14390,7 @@ return /******/ (function(modules) { // webpackBootstrap
topojson: true
};
options = (0, _lodashAssign2['default'])(defaults, options);
options = (0, _lodashAssign2['default'])({}, defaults, options);
return (0, _GeoJSONTileLayer2['default'])(path, options);
};
@ -14341,6 +14398,393 @@ return /******/ (function(modules) { // webpackBootstrap
;
module.exports = exports['default'];
/***/ },
/* 65 */
/***/ function(module, exports, __webpack_require__) {
Object.defineProperty(exports, '__esModule', {
value: true
});
var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; desc = parent = undefined; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } };
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
var _Layer2 = __webpack_require__(33);
var _Layer3 = _interopRequireDefault(_Layer2);
var _three = __webpack_require__(24);
var _three2 = _interopRequireDefault(_three);
var _reqwest = __webpack_require__(55);
var _reqwest2 = _interopRequireDefault(_reqwest);
var _lodashAssign = __webpack_require__(3);
var _lodashAssign2 = _interopRequireDefault(_lodashAssign);
var _geoPoint = __webpack_require__(11);
var _geoPoint2 = _interopRequireDefault(_geoPoint);
var _geoLatLon = __webpack_require__(10);
var _geoLatLon2 = _interopRequireDefault(_geoLatLon);
var _utilGeoJSON = __webpack_require__(57);
var _utilGeoJSON2 = _interopRequireDefault(_utilGeoJSON);
var _utilBuffer = __webpack_require__(63);
var _utilBuffer2 = _interopRequireDefault(_utilBuffer);
var GeoJSONLayer = (function (_Layer) {
_inherits(GeoJSONLayer, _Layer);
function GeoJSONLayer(geojson, options) {
_classCallCheck(this, GeoJSONLayer);
_get(Object.getPrototypeOf(GeoJSONLayer.prototype), 'constructor', this).call(this, options);
this._geojson = geojson;
this._defaultStyle = _utilGeoJSON2['default'].defaultStyle;
var defaults = {
topojson: false,
filter: null,
style: this._defaultStyle
};
this._options = (0, _lodashAssign2['default'])({}, defaults, options);
if (typeof options.style === 'function') {
this._options.style = options.style;
} else {
this._options.style = (0, _lodashAssign2['default'])({}, defaults.style, options.style);
}
}
// Initialise without requiring new keyword
_createClass(GeoJSONLayer, [{
key: '_onAdd',
value: function _onAdd(world) {
// Request data from URL if needed
if (typeof this._geojson === 'string') {
this._requestData(this._geojson);
} else {
// Process and add GeoJSON to layer
this._processData(this._geojson);
}
}
}, {
key: '_requestData',
value: function _requestData(url) {
var _this = this;
this._request = (0, _reqwest2['default'])({
url: url,
type: 'json',
crossOrigin: true
}).then(function (res) {
// Clear request reference
_this._request = null;
_this._processData(res);
})['catch'](function (err) {
console.error(err);
// Clear request reference
_this._request = null;
});
}
}, {
key: '_processData',
value: function _processData(data) {
var _this2 = this;
console.time('GeoJSON');
console.log(data);
var geojson = _utilGeoJSON2['default'].mergeFeatures(data, this._options.topojson);
// TODO: Check that GeoJSON is valid / usable
var features = geojson.features;
// Run filter, if provided
if (this._options.filter) {
features = geojson.features.filter(this._options.filter);
}
var style = this._options.style;
var offset;
// TODO: Wrap into a helper method so this isn't duplicated in the tiled
// GeoJSON output layer
//
// Need to be careful as to not make it impossible to fork this off into a
// worker script at a later stage
//
// Also unsure as to whether it's wise to lump so much into a black box
//
// var meshes = GeoJSON.createMeshes(features, offset, style);
var polygons = {
vertices: [],
faces: [],
colours: [],
facesCount: 0,
allFlat: true
};
var lines = {
vertices: [],
colours: [],
verticesCount: 0
};
var colour = new _three2['default'].Color();
features.forEach(function (feature) {
// feature.geometry, feature.properties
// Skip features that aren't supported
//
// TODO: Add support for all GeoJSON geometry types, including Multi...
// geometry types
if (feature.geometry.type !== 'Polygon' && feature.geometry.type !== 'LineString' && feature.geometry.type !== 'MultiLineString') {
return;
}
// Get style object, if provided
if (typeof _this2._options.style === 'function') {
style = (0, _lodashAssign2['default'])(_this2._defaultStyle, _this2._options.style(feature));
}
var coordinates = feature.geometry.coordinates;
// if (feature.geometry.type === 'LineString') {
if (feature.geometry.type === 'LineString') {
colour.set(style.lineColor);
coordinates = coordinates.map(function (coordinate) {
var latlon = (0, _geoLatLon2['default'])(coordinate[1], coordinate[0]);
var point = _this2._world.latLonToPoint(latlon);
if (!offset) {
offset = (0, _geoPoint2['default'])(0, 0);
offset.x = -1 * point.x;
offset.y = -1 * point.y;
_this2._pointScale = _this2._world.pointScale(latlon);
}
return [point.x, point.y];
});
var height = 0;
if (style.lineHeight) {
height = _this2._world.metresToWorld(style.lineHeight, _this2._pointScale);
}
var linestringAttributes = _utilGeoJSON2['default'].lineStringAttributes(coordinates, colour, height);
lines.vertices.push(linestringAttributes.vertices);
lines.colours.push(linestringAttributes.colours);
lines.verticesCount += linestringAttributes.vertices.length;
}
if (feature.geometry.type === 'MultiLineString') {
colour.set(style.lineColor);
coordinates = coordinates.map(function (_coordinates) {
return _coordinates.map(function (coordinate) {
var latlon = (0, _geoLatLon2['default'])(coordinate[1], coordinate[0]);
var point = _this2._world.latLonToPoint(latlon);
if (!offset) {
offset = (0, _geoPoint2['default'])(0, 0);
offset.x = -1 * point.x;
offset.y = -1 * point.y;
_this2._pointScale = _this2._world.pointScale(latlon);
}
return [point.x, point.y];
});
});
var height = 0;
if (style.lineHeight) {
height = _this2._world.metresToWorld(style.lineHeight, _this2._pointScale);
}
var multiLinestringAttributes = _utilGeoJSON2['default'].multiLineStringAttributes(coordinates, colour, height);
lines.vertices.push(multiLinestringAttributes.vertices);
lines.colours.push(multiLinestringAttributes.colours);
lines.verticesCount += multiLinestringAttributes.vertices.length;
}
if (feature.geometry.type === 'Polygon') {
colour.set(style.color);
coordinates = coordinates.map(function (ring) {
return ring.map(function (coordinate) {
var latlon = (0, _geoLatLon2['default'])(coordinate[1], coordinate[0]);
var point = _this2._world.latLonToPoint(latlon);
if (!offset) {
offset = (0, _geoPoint2['default'])(0, 0);
offset.x = -1 * point.x;
offset.y = -1 * point.y;
_this2._pointScale = _this2._world.pointScale(latlon);
}
return [point.x, point.y];
});
});
var height = 0;
if (style.height) {
height = _this2._world.metresToWorld(style.height, _this2._pointScale);
}
var polygonAttributes = _utilGeoJSON2['default'].polygonAttributes(coordinates, colour, height);
polygons.vertices.push(polygonAttributes.vertices);
polygons.faces.push(polygonAttributes.faces);
polygons.colours.push(polygonAttributes.colours);
if (polygons.allFlat && !polygonAttributes.flat) {
polygons.allFlat = false;
}
polygons.facesCount += polygonAttributes.faces.length;
}
});
var geometry;
var material;
var mesh;
// Output lines
if (lines.vertices.length > 0) {
geometry = _utilBuffer2['default'].createLineGeometry(lines, offset);
material = new _three2['default'].LineBasicMaterial({
vertexColors: _three2['default'].VertexColors,
linewidth: style.lineWidth,
transparent: style.lineTransparent,
opacity: style.lineOpacity,
blending: style.lineBlending
});
mesh = new _three2['default'].LineSegments(geometry, material);
if (style.lineRenderOrder !== undefined) {
material.depthWrite = false;
mesh.renderOrder = style.lineRenderOrder;
}
// TODO: Can a line cast a shadow?
// mesh.castShadow = true;
this.add(mesh);
}
// Output polygons
if (polygons.facesCount > 0) {
geometry = _utilBuffer2['default'].createGeometry(polygons, offset);
if (!this._world._environment._skybox) {
material = new _three2['default'].MeshPhongMaterial({
vertexColors: _three2['default'].VertexColors,
side: _three2['default'].BackSide
});
} else {
material = new _three2['default'].MeshStandardMaterial({
vertexColors: _three2['default'].VertexColors,
side: _three2['default'].BackSide
});
material.roughness = 1;
material.metalness = 0.1;
material.envMapIntensity = 3;
material.envMap = this._world._environment._skybox.getRenderTarget();
}
mesh = new _three2['default'].Mesh(geometry, material);
mesh.castShadow = true;
mesh.receiveShadow = true;
if (polygons.allFlat) {
material.depthWrite = false;
mesh.renderOrder = 1;
}
this.add(mesh);
}
// Move layer to origin Point
//
// TODO: Is there a better way to ensure everything is aligned right and
// able to be frustum-culled?
this._layer.position.x = -offset.x;
this._layer.position.z = -offset.y;
console.timeEnd('GeoJSON');
}
}, {
key: '_abortRequest',
value: function _abortRequest() {
if (!this._request) {
return;
}
this._request.abort();
}
}, {
key: 'destroy',
value: function destroy() {
// Cancel any pending requests
this._abortRequest();
// Clear request reference
this._request = null;
// Run common destruction logic from parent
_get(Object.getPrototypeOf(GeoJSONLayer.prototype), 'destroy', this).call(this);
}
}]);
return GeoJSONLayer;
})(_Layer3['default']);
exports['default'] = function (geojson, options) {
return new GeoJSONLayer(geojson, options);
};
;
module.exports = exports['default'];
/***/ }
/******/ ])
});

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Wyświetl plik

@ -35,7 +35,7 @@ var topoJSONTileLayer = VIZI.TopoJSONTileLayer('https://vector.mapzen.com/osm/bu
attribution: '&copy; <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors, <a href="http://whosonfirst.mapzen.com#License">Who\'s On First</a>.'
}).addTo(world);
// Just buildings from Mapzen (polygons)
// // Just buildings from Mapzen (polygons)
// var topoJSONTileLayer = VIZI.TopoJSONTileLayer('https://vector.mapzen.com/osm/buildings/{z}/{x}/{y}.topojson?api_key=vector-tiles-NT5Emiw', {
// style: function(feature) {
// var height;
@ -59,3 +59,17 @@ var topoJSONTileLayer = VIZI.TopoJSONTileLayer('https://vector.mapzen.com/osm/bu
// // },
// attribution: '&copy; <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors, <a href="http://whosonfirst.mapzen.com#License">Who\'s On First</a>.'
// }).addTo(world);
// // London Underground lines
// var geoJSONLayer = VIZI.GeoJSONLayer('https://rawgit.com/robhawkes/4acb9d6a6a5f00a377e2/raw/30ae704a44e10f2e13fb7e956e80c3b22e8e7e81/tfl_lines.json', {
// style: {
// lineColor: '#f7c616',
// // lineHeight: 20,
// lineWidth: 1,
// lineTransparent: true,
// lineOpacity: 0.5,
// lineBlending: THREE.AdditiveBlending,
// lineRenderOrder: 2
// },
// attribution: '&copy; Transport for London.'
// }).addTo(world);

Wyświetl plik

@ -19,7 +19,7 @@ class World extends EventEmitter {
skybox: false
};
this.options = extend(defaults, options);
this.options = extend({}, defaults, options);
this._layers = [];
this._controls = [];

Wyświetl plik

@ -0,0 +1,328 @@
import Layer from './Layer';
import THREE from 'three';
import reqwest from 'reqwest';
import extend from 'lodash.assign';
import Point from '../geo/Point';
import LatLon from '../geo/LatLon';
import GeoJSON from '../util/GeoJSON';
import Buffer from '../util/Buffer';
class GeoJSONLayer extends Layer {
constructor(geojson, options) {
super(options);
this._geojson = geojson;
this._defaultStyle = GeoJSON.defaultStyle;
var defaults = {
topojson: false,
filter: null,
style: this._defaultStyle
};
this._options = extend({}, defaults, options);
if (typeof options.style === 'function') {
this._options.style = options.style;
} else {
this._options.style = extend({}, defaults.style, options.style);
}
}
_onAdd(world) {
// Request data from URL if needed
if (typeof this._geojson === 'string') {
this._requestData(this._geojson);
} else {
// Process and add GeoJSON to layer
this._processData(this._geojson);
}
}
_requestData(url) {
this._request = reqwest({
url: url,
type: 'json',
crossOrigin: true
}).then(res => {
// Clear request reference
this._request = null;
this._processData(res);
}).catch(err => {
console.error(err);
// Clear request reference
this._request = null;
});
}
_processData(data) {
console.time('GeoJSON');
console.log(data);
var geojson = GeoJSON.mergeFeatures(data, this._options.topojson);
// TODO: Check that GeoJSON is valid / usable
var features = geojson.features;
// Run filter, if provided
if (this._options.filter) {
features = geojson.features.filter(this._options.filter);
}
var style = this._options.style;
var offset;
// TODO: Wrap into a helper method so this isn't duplicated in the tiled
// GeoJSON output layer
//
// Need to be careful as to not make it impossible to fork this off into a
// worker script at a later stage
//
// Also unsure as to whether it's wise to lump so much into a black box
//
// var meshes = GeoJSON.createMeshes(features, offset, style);
var polygons = {
vertices: [],
faces: [],
colours: [],
facesCount: 0,
allFlat: true
};
var lines = {
vertices: [],
colours: [],
verticesCount: 0
};
var colour = new THREE.Color();
features.forEach(feature => {
// feature.geometry, feature.properties
// Skip features that aren't supported
//
// TODO: Add support for all GeoJSON geometry types, including Multi...
// geometry types
if (
feature.geometry.type !== 'Polygon' &&
feature.geometry.type !== 'LineString' &&
feature.geometry.type !== 'MultiLineString'
) {
return;
}
// Get style object, if provided
if (typeof this._options.style === 'function') {
style = extend(this._defaultStyle, this._options.style(feature));
}
var coordinates = feature.geometry.coordinates;
// if (feature.geometry.type === 'LineString') {
if (feature.geometry.type === 'LineString') {
colour.set(style.lineColor);
coordinates = coordinates.map(coordinate => {
var latlon = LatLon(coordinate[1], coordinate[0]);
var point = this._world.latLonToPoint(latlon);
if (!offset) {
offset = Point(0, 0);
offset.x = -1 * point.x;
offset.y = -1 * point.y;
this._pointScale = this._world.pointScale(latlon);
}
return [point.x, point.y];
});
var height = 0;
if (style.lineHeight) {
height = this._world.metresToWorld(style.lineHeight, this._pointScale);
}
var linestringAttributes = GeoJSON.lineStringAttributes(coordinates, colour, height);
lines.vertices.push(linestringAttributes.vertices);
lines.colours.push(linestringAttributes.colours);
lines.verticesCount += linestringAttributes.vertices.length;
}
if (feature.geometry.type === 'MultiLineString') {
colour.set(style.lineColor);
coordinates = coordinates.map(_coordinates => {
return _coordinates.map(coordinate => {
var latlon = LatLon(coordinate[1], coordinate[0]);
var point = this._world.latLonToPoint(latlon);
if (!offset) {
offset = Point(0, 0);
offset.x = -1 * point.x;
offset.y = -1 * point.y;
this._pointScale = this._world.pointScale(latlon);
}
return [point.x, point.y];
});
});
var height = 0;
if (style.lineHeight) {
height = this._world.metresToWorld(style.lineHeight, this._pointScale);
}
var multiLinestringAttributes = GeoJSON.multiLineStringAttributes(coordinates, colour, height);
lines.vertices.push(multiLinestringAttributes.vertices);
lines.colours.push(multiLinestringAttributes.colours);
lines.verticesCount += multiLinestringAttributes.vertices.length;
}
if (feature.geometry.type === 'Polygon') {
colour.set(style.color);
coordinates = coordinates.map(ring => {
return ring.map(coordinate => {
var latlon = LatLon(coordinate[1], coordinate[0]);
var point = this._world.latLonToPoint(latlon);
if (!offset) {
offset = Point(0, 0);
offset.x = -1 * point.x;
offset.y = -1 * point.y;
this._pointScale = this._world.pointScale(latlon);
}
return [point.x, point.y];
});
});
var height = 0;
if (style.height) {
height = this._world.metresToWorld(style.height, this._pointScale);
}
var polygonAttributes = GeoJSON.polygonAttributes(coordinates, colour, height);
polygons.vertices.push(polygonAttributes.vertices);
polygons.faces.push(polygonAttributes.faces);
polygons.colours.push(polygonAttributes.colours);
if (polygons.allFlat && !polygonAttributes.flat) {
polygons.allFlat = false;
}
polygons.facesCount += polygonAttributes.faces.length;
}
});
var geometry;
var material;
var mesh;
// Output lines
if (lines.vertices.length > 0) {
geometry = Buffer.createLineGeometry(lines, offset);
material = new THREE.LineBasicMaterial({
vertexColors: THREE.VertexColors,
linewidth: style.lineWidth,
transparent: style.lineTransparent,
opacity: style.lineOpacity,
blending: style.lineBlending
});
mesh = new THREE.LineSegments(geometry, material);
if (style.lineRenderOrder !== undefined) {
material.depthWrite = false;
mesh.renderOrder = style.lineRenderOrder;
}
// TODO: Can a line cast a shadow?
// mesh.castShadow = true;
this.add(mesh);
}
// Output polygons
if (polygons.facesCount > 0) {
geometry = Buffer.createGeometry(polygons, offset);
if (!this._world._environment._skybox) {
material = new THREE.MeshPhongMaterial({
vertexColors: THREE.VertexColors,
side: THREE.BackSide
});
} else {
material = new THREE.MeshStandardMaterial({
vertexColors: THREE.VertexColors,
side: THREE.BackSide
});
material.roughness = 1;
material.metalness = 0.1;
material.envMapIntensity = 3;
material.envMap = this._world._environment._skybox.getRenderTarget();
}
mesh = new THREE.Mesh(geometry, material);
mesh.castShadow = true;
mesh.receiveShadow = true;
if (polygons.allFlat) {
material.depthWrite = false;
mesh.renderOrder = 1;
}
this.add(mesh);
}
// Move layer to origin Point
//
// TODO: Is there a better way to ensure everything is aligned right and
// able to be frustum-culled?
this._layer.position.x = -offset.x;
this._layer.position.z = -offset.y;
console.timeEnd('GeoJSON');
}
_abortRequest() {
if (!this._request) {
return;
}
this._request.abort();
}
destroy() {
// Cancel any pending requests
this._abortRequest();
// Clear request reference
this._request = null;
// Run common destruction logic from parent
super.destroy();
}
}
// Initialise without requiring new keyword
export default function(geojson, options) {
return new GeoJSONLayer(geojson, options);
};

Wyświetl plik

@ -43,15 +43,7 @@ class GeoJSONTile extends Tile {
constructor(quadcode, path, layer, options) {
super(quadcode, path, layer);
this._defaultStyle = {
color: '#ffffff',
height: 0,
lineOpacity: 1,
lineTransparent: false,
lineColor: '#ffffff',
lineWidth: 1,
lineBlending: THREE.NormalBlending
};
this._defaultStyle = GeoJSON.defaultStyle;
var defaults = {
topojson: false,
@ -59,7 +51,13 @@ class GeoJSONTile extends Tile {
style: this._defaultStyle
};
this._options = extend(defaults, options);
this._options = extend({}, defaults, options);
if (typeof options.style === 'function') {
this._options.style = options.style;
} else {
this._options.style = extend({}, defaults.style, options.style);
}
}
// Request data for the tile
@ -296,7 +294,7 @@ class GeoJSONTile extends Tile {
// Get style object, if provided
if (typeof this._options.style === 'function') {
style = extend(this._defaultStyle, this._options.style(feature));
style = extend({}, this._defaultStyle, this._options.style(feature));
}
var coordinates = feature.geometry.coordinates;
@ -311,7 +309,13 @@ class GeoJSONTile extends Tile {
return [point.x, point.y];
});
var linestringAttributes = GeoJSON.lineStringAttributes(coordinates, colour);
var height = 0;
if (style.lineHeight) {
height = this._world.metresToWorld(style.lineHeight, this._pointScale);
}
var linestringAttributes = GeoJSON.lineStringAttributes(coordinates, colour, height);
lines.vertices.push(linestringAttributes.vertices);
lines.colours.push(linestringAttributes.colours);
@ -329,7 +333,13 @@ class GeoJSONTile extends Tile {
});
});
var multiLinestringAttributes = GeoJSON.multiLineStringAttributes(coordinates, colour);
var height = 0;
if (style.lineHeight) {
height = this._world.metresToWorld(style.lineHeight, this._pointScale);
}
var multiLinestringAttributes = GeoJSON.multiLineStringAttributes(coordinates, colour, height);
lines.vertices.push(multiLinestringAttributes.vertices);
lines.colours.push(multiLinestringAttributes.colours);
@ -418,11 +428,15 @@ class GeoJSONTile extends Tile {
//
// this._mesh.add(mesh);
var geometry;
var material;
var mesh;
// Output lines
if (lines.vertices.length > 0) {
var geometry = Buffer.createLineGeometry(lines, offset);
geometry = Buffer.createLineGeometry(lines, offset);
var material = new THREE.LineBasicMaterial({
material = new THREE.LineBasicMaterial({
vertexColors: THREE.VertexColors,
linewidth: style.lineWidth,
transparent: style.lineTransparent,
@ -430,9 +444,10 @@ class GeoJSONTile extends Tile {
blending: style.lineBlending
});
var mesh = new THREE.LineSegments(geometry, material);
mesh = new THREE.LineSegments(geometry, material);
if (style.lineRenderOrder) {
if (style.lineRenderOrder !== undefined) {
material.depthWrite = false;
mesh.renderOrder = style.lineRenderOrder;
}
@ -444,9 +459,8 @@ class GeoJSONTile extends Tile {
// Output polygons
if (polygons.facesCount > 0) {
var geometry = Buffer.createGeometry(polygons, offset);
geometry = Buffer.createGeometry(polygons, offset);
var material;
if (!this._world._environment._skybox) {
material = new THREE.MeshPhongMaterial({
vertexColors: THREE.VertexColors,
@ -463,13 +477,13 @@ class GeoJSONTile extends Tile {
material.envMap = this._world._environment._skybox.getRenderTarget();
}
var mesh = new THREE.Mesh(geometry, material);
mesh = new THREE.Mesh(geometry, material);
mesh.castShadow = true;
mesh.receiveShadow = true;
if (polygons.allFlat) {
// This is only useful for flat objects
material.depthWrite = false;
mesh.renderOrder = 1;
}

Wyświetl plik

@ -36,7 +36,7 @@ class GeoJSONTileLayer extends TileLayer {
distance: 2000
};
options = extend(defaults, options);
options = extend({}, defaults, options);
super(options);

Wyświetl plik

@ -62,7 +62,7 @@ class ImageTileLayer extends TileLayer {
distance: 40000
};
options = extend(defaults, options);
options = extend({}, defaults, options);
super(options);

Wyświetl plik

@ -54,7 +54,7 @@ class TileLayer extends Layer {
maxLOD: 18
};
this._options = extend(defaults, options);
this._options = extend({}, defaults, options);
this._tileCache = TileCache(this._options.maxCache, tile => {
this._destroyTile(tile);

Wyświetl plik

@ -7,7 +7,7 @@ export default function(path, options) {
topojson: true
};
options = extend(defaults, options);
options = extend({}, defaults, options);
return GeoJSONTileLayer(path, options);
};

Wyświetl plik

@ -8,11 +8,24 @@ import geojsonMerge from 'geojson-merge';
import earcut from 'earcut';
import extrudePolygon from './extrudePolygon';
// TODO: Make it so height can be per-coordinate for linestrings so you can do
// things like offsetting GPS points based on elevation at each point
// Light and dark colours used for poor-mans AO gradient on object sides
var light = new THREE.Color(0xffffff);
var shadow = new THREE.Color(0x666666);
var GeoJSON = (function() {
var defaultStyle = {
color: '#ffffff',
height: 0,
lineOpacity: 1,
lineTransparent: false,
lineColor: '#ffffff',
lineWidth: 1,
lineBlending: THREE.NormalBlending
};
// Attempts to merge together multiple GeoJSON Features or FeatureCollections
// into a single FeatureCollection
var mergeFeatures = function(data, _topojson) {
@ -43,13 +56,15 @@ var GeoJSON = (function() {
}
return geojsonMerge(collections);
} else {
} else if (Array.isArray(data)) {
return geojsonMerge(data);
} else {
return data;
}
}
};
var lineStringAttributes = function(coordinates, colour) {
var lineStringAttributes = function(coordinates, colour, height) {
var _coords = [];
var _colours = [];
@ -62,12 +77,12 @@ var GeoJSON = (function() {
coordinates.forEach((coordinate, index) => {
// TODO: Don't hardcode y-value
_colours.push([colour.r, colour.g, colour.b]);
_coords.push([coordinate[0], 0, coordinate[1]]);
_coords.push([coordinate[0], height, coordinate[1]]);
nextCoord = (coordinates[index + 1]) ? coordinates[index + 1] : coordinate;
_colours.push([colour.r, colour.g, colour.b]);
_coords.push([nextCoord[0], 0, nextCoord[1]]);
_coords.push([nextCoord[0], height, nextCoord[1]]);
});
return {
@ -76,13 +91,13 @@ var GeoJSON = (function() {
};
};
var multiLineStringAttributes = function(coordinates, colour) {
var multiLineStringAttributes = function(coordinates, colour, height) {
var _coords = [];
var _colours = [];
var result;
coordinates.forEach(coordinate => {
result = lineStringAttributes(coordinate, colour);
result = lineStringAttributes(coordinate, colour, height);
result.vertices.forEach(coord => {
_coords.push(coord);
@ -209,6 +224,7 @@ var GeoJSON = (function() {
};
return {
defaultStyle: defaultStyle,
mergeFeatures: mergeFeatures,
lineStringAttributes: lineStringAttributes,
multiLineStringAttributes: multiLineStringAttributes,

Wyświetl plik

@ -14,7 +14,7 @@ var extrudePolygon = function(points, faces, _options) {
closed: true
};
var options = extend(defaults, _options);
var options = extend({}, defaults, _options);
var n = points.length;
var positions;

Wyświetl plik

@ -4,6 +4,7 @@ import EnvironmentLayer from './layer/environment/EnvironmentLayer';
import ImageTileLayer from './layer/tile/ImageTileLayer';
import GeoJSONTileLayer from './layer/tile/GeoJSONTileLayer';
import TopoJSONTileLayer from './layer/tile/TopoJSONTileLayer';
import GeoJSONLayer from './layer/GeoJSONLayer';
import Point from './geo/Point';
import LatLon from './geo/LatLon';
@ -17,6 +18,7 @@ const VIZI = {
ImageTileLayer: ImageTileLayer,
GeoJSONTileLayer: GeoJSONTileLayer,
TopoJSONTileLayer: TopoJSONTileLayer,
GeoJSONLayer: GeoJSONLayer,
Point: Point,
LatLon: LatLon
};