UNPKG

argon-osm-occlusion

Version:

Occlusion handling using OpenStreetMap building data in an Argon.js environment.

387 lines (331 loc) 11.7 kB
'use strict'; 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 _three = require('three'); var THREE = _interopRequireWildcard(_three); var _argon = require('@argonjs/argon'); var Argon = _interopRequireWildcard(_argon); function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } var app, scene; var ArgonOsmOcclusion = function () { /** * @constructor * @param {app} Argon app * @param {scene} THREE scene */ function ArgonOsmOcclusion(_app, _scene) { _classCallCheck(this, ArgonOsmOcclusion); app = _app; scene = _scene; this.debugMode = false; this.featureGroups = []; // Bind this to methods this.add.bind(this); this.remove.bind(this); this.removeAll.bind(this); this.setDebug.bind(this); app.updateEvent.addEventListener(this.update.bind(this)); } /** * @method * @name add * @description Function for adding occlusion with real world buildings from OSM around coordinate * @param {options} Options * {longitude} Coordinate longitude * {latitude} Coordinate latitude * {altitude} Altitude of the added objects. Default 0. Optional. * {radius} Radius of bounding circle when querying for OSM features. Default 500 m. Optional * {name} Name of this feature group. Optional. * {levels} Fallback number of levels if tag is not available on feature in Overpass API. Optional. * @param {callback} Callback with id of the feature group or error. Optional. */ _createClass(ArgonOsmOcclusion, [{ key: 'add', value: function add(options, callback) { if (options.longitude == null || options.latitude == null) { if (callback) { callback({ message: "Wrong params provided to method. Lng, lat, alt, r required." }); } return; } options.altitude = options.altitude != null ? options.altitude : 0; options.radius = options.radius != null ? options.radius : 500; options.levels = options.levels != null ? options.levels : 3; var boundingCircle = '(around:' + options.radius + ',' + options.latitude + ',' + options.longitude + ')'; var query = '[out:json];(way["building"]' + boundingCircle + ';relation["building"]' + boundingCircle + ';);out body;>;out skel qt;'; query_overpass(query, function (error, data) { if (error) { if (callback) { return callback(error, undefined); } return; } var features = []; data.features.forEach(function (osmFeature) { features.push(createFeatureEntities(osmFeature, options.altitude, options.levels)); }); var featureGroup = { id: this.featureGroups.length, name: options.name, longitude: options.longitude, latitude: options.latitude, altitude: options.altitude, radius: options.radius, levels: options.levels, features: features, geoObjects: [], geoEntities: [] }; this.featureGroups.push(featureGroup); if (callback) { callback(undefined, featureGroup.id); } }.bind(this)); } /** * @method * @name remove * @description Function for removing all occlusion objects tied to featureGroup in scene * @param {id} Id of the feature group which associated occlusion objects should be removed */ }, { key: 'remove', value: function remove(id) { var featureGroups = this.featureGroups.filter(function (group) { return group.id === id; }); if (featureGroups[0] != null) { featureGroups[0].geoObjects.forEach(function (obj) { scene.remove(obj); }.bind(this)); this.featureGroups = this.featureGroups.filter(function (group) { return group.is !== id; }); } else { console.log("ArgonOsmOcclusion: Cannot remove feature group with id " + id + ", it does not exist."); } } /** * @method * @name removeAll * @description Function for removing all occlusion objects in scene */ }, { key: 'removeAll', value: function removeAll() { var temp = this.featureGroups; temp.forEach(function (group) { this.remove(group.id); }.bind(this)); } /** * @method * @name enable * @description Enables all buildings in the scene */ }, { key: 'enable', value: function enable() { this.featureGroups.forEach(function (group) { group.geoObjects.forEach(function (object) { object.visible = true; }); }); } /** * @method * @name disable * @description Disables all buildings in the scene */ }, { key: 'disable', value: function disable() { this.featureGroups.forEach(function (group) { group.geoObjects.forEach(function (object) { object.visible = false; }); }); } /** * @method * @name setDebug * @description Shows/hides all the OSM buildings in the scene * @param {debug} Bool */ }, { key: 'setDebug', value: function setDebug(debug) { this.debugMode = debug; this.featureGroups.forEach(function (group) { group.geoObjects.forEach(function (geoObject) { geoObject.children[0].material.colorWrite = debug; }); }); } /** * @method * @name update * @description Triggered on every Argon UpdateEvent */ }, { key: 'update', value: function update() { this.featureGroups.forEach(function (group) { if (group.geoObjects.length < group.features.length) { group.features.forEach(function (feature) { if (feature.hasCreatedGeometry == false) { var result = createGeometry(feature, this.debugMode, app.context, scene); if (result !== null) { group.geoEntities.push(result.geoEntity); group.geoObjects.push(result.geoObject); } } }.bind(this)); } else { group.geoEntities.forEach(function (geoEntity, index) { var targetPose = app.context.getEntityPose(geoEntity); group.geoObjects[index].position.copy(targetPose.position); }.bind(this)); } }.bind(this)); } }]); return ArgonOsmOcclusion; }(); exports.default = ArgonOsmOcclusion; function createFeatureEntities(osmFeature, alt, levelsFallback) { if (osmFeature.geometry.type !== "Polygon") { return; } var feature = { shape: [], holes: [], hasCreatedGeometry: false, position: null, height: 0 }; osmFeature.geometry.coordinates.forEach(function (coordinateGroup, index) { var entities = []; coordinateGroup.forEach(function (coordinate, index) { var geoEntity = new Argon.Cesium.Entity({ name: "", position: Argon.Cesium.Cartesian3.fromDegrees(coordinate[0], coordinate[1], alt), orientation: Argon.Cesium.Quaternion.IDENTITY }); entities.push(geoEntity); }); if (index == 0) { feature.shape = entities; feature.position = entities[0].position; } else { feature.holes.push(entities); } }); var levels = levelsFallback; if (osmFeature.properties.tags['building:levels']) { levels = parseInt(osmFeature.properties.tags['building:levels']); } if (osmFeature.properties.tags['roof:shape'] && osmFeature.properties.tags['roof:shape'] !== 'flat') { levels += 1; } feature.height = levels * 3; return feature; } function getVertices(entites, context) { var vertices = []; entites.forEach(function (entity) { var geoPos = context.getEntityPose(entity); if (geoPos.poseStatus == 0) { return; } var v = new THREE.Vector2(geoPos.position.x, geoPos.position.z); vertices.push(v); }); return vertices; } function createGeometry(feature, debug, context, scene) { var vertices = getVertices(feature.shape, context); if (vertices.length == 0) { return null; } var holes = []; feature.holes.forEach(function (holeEntites) { holes.concat(getVertices(holeEntites, context)); }); var shape = new THREE.Shape(vertices, holes); var extrudeSettings = { amount: feature.height, step: 1, bevelEnabled: false }; var geometry = new THREE.ExtrudeGeometry(shape, extrudeSettings); geometry.center(); geometry.rotateX(Math.PI / 2); geometry.applyMatrix(new THREE.Matrix4().makeTranslation(-geometry.vertices[0].x, geometry.vertices[0].y, -geometry.vertices[0].z)); var material = new THREE.MeshPhongMaterial({ color: 0x0000ff, colorWrite: debug, side: THREE.DoubleSide, shading: THREE.FlatShading }); var mesh = new THREE.Mesh(geometry, material); mesh.renderOrder = 1; var geoObject = new THREE.Object3D(); geoObject.add(mesh); scene.add(geoObject); var geoEntity = new Argon.Cesium.Entity({ name: "", position: feature.position, orientation: Argon.Cesium.Quaternion.IDENTITY }); feature.hasCreatedGeometry = true; return { geoObject: geoObject, geoEntity: geoEntity }; } 'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); exports.query_overpass = query_overpass; var _osmtogeojson = require('osmtogeojson'); var _osmtogeojson2 = _interopRequireDefault(_osmtogeojson); var _request = require('request'); var _request2 = _interopRequireDefault(_request); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function query_overpass(query, cb, options) { options = options || {}; var defaultUrl; if (window.location.protocol === 'https:') { defaultUrl = 'https://overpass-api.de:443/api/interpreter'; } else { defaultUrl = 'http://overpass-api.de:80/api/interpreter'; } _request2.default.post({ url: options.overpassUrl || defaultUrl, withCredentials: false }, function (error, response, body) { var geojson; if (!error && response.statusCode === 200) { geojson = (0, _osmtogeojson2.default)(JSON.parse(body), { flatProperties: options.flatProperties || false }); cb(undefined, geojson); } else if (error) { cb(error); } else if (response) { cb({ message: 'Request failed: HTTP ' + response.statusCode, statusCode: response.statusCode }); } else { cb({ message: 'Unknown error.' }); } }).form({ data: query }); };