diff --git a/examples/google-tiles/index.html b/examples/google-tiles/index.html index 7510f3af8..a3ca2d26d 100644 --- a/examples/google-tiles/index.html +++ b/examples/google-tiles/index.html @@ -100,21 +100,12 @@ - - - - + diff --git a/package.json b/package.json index 77385c975..0bc34f638 100644 --- a/package.json +++ b/package.json @@ -12,9 +12,9 @@ "prefirebase": "cp -R assets public && cp -R ui_assets public && cp index.html public && cp -R dist public", "prepare": "husky install", "prepublish": "npm run dist", + "test": "nyc --reporter=lcov --reporter=text mocha --recursive --full-trace test/*.test.js", + "test:watch": "mocha --recursive --full-trace --watch test/*.test.js", "prettier": "prettier --write 'src/**/*.js' 'src/**/*.jsx'", - "test": "nyc --reporter=lcov --reporter=text mocha --recursive --full-trace", - "test:watch": "mocha --recursive --full-trace --watch", "deploy": "npm run prefirebase && cd public && firebase deploy --only hosting:app3dstreet", "storybook": "storybook dev -p 6006", "build-storybook": "storybook build" diff --git a/src/components/README.md b/src/components/README.md new file mode 100644 index 000000000..97a2343be --- /dev/null +++ b/src/components/README.md @@ -0,0 +1,14 @@ +# Docs for custom A-Frame components used with 3DStreet + +## Street-geo component + +The components accept longitude, latitude, elevation and an array of map types to indicate which child maps to spawn. Possible values for maps array: 'mapbox2d', 'google3d'. + + The component assigns the class 'autocreated' to its child elements. All attribute values can be changed at runtime and the component will update the child elements (map entities) and their corresponding parameters. The 'elevation' attribute is only used for the 'google3d' tiles element for now. + +To add support for a new map type, you need to take the following steps: +* add map name to this.mapTypes variable +* add creating function with name: `Create` +* add update function with name: `Update` + +It is assumed that the appropriate libraries for all map types are loaded in advance. \ No newline at end of file diff --git a/src/components/street-geo.js b/src/components/street-geo.js new file mode 100644 index 000000000..6673e13ae --- /dev/null +++ b/src/components/street-geo.js @@ -0,0 +1,118 @@ +/* global AFRAME, THREE */ +const MAPBOX_ACCESS_TOKEN_VALUE = 'pk.eyJ1Ijoia2llcmFuZmFyciIsImEiOiJjazB0NWh2YncwOW9rM25sd2p0YTlxemk2In0.mLl4sNGDFbz_QXk0GIK02Q'; +const GOOGLE_API_KEY = 'AIzaSyAQshwLVKTpwTfPJxFEkEzOdP_cgmixTCQ'; + +/* + * Street-geo component + * + * the component accept longitude, latitude, elevation and an array of map types to indicate + * which child maps to spawn. Possible values for maps array: 'mapbox2d', 'google3d'. + * The component assigns the class 'autocreated' to its child elements. + * All attribute values can be changed at runtime and the component will update + * the child elements (map entities) and their corresponding parameters. + * The 'elevation' attribute is only used for the 'google3d' tiles element for now. + * + * to add support for a new map type, you need to take the following steps: + * - add map name to this.mapTypes variable + * - add creating function with name: Create + * - add update function with name: Update + * + * It is assumed that the appropriate libraries for all map types are loaded in advance. + */ +AFRAME.registerComponent('street-geo', { + schema: { + longitude: { type: 'number', default: 0 }, + latitude: { type: 'number', default: 0 }, + elevation: { type: 'number', default: 0 }, + maps: { type: 'array', default: [] } + }, + init: function () { + /* + Function names for the given function types must have the following format: + create function: Create, + update function: Update, + */ + this.mapTypes = ['mapbox2d', 'google3d']; + this.elevationHeightConstant = 32.49158; + }, + update: function (oldData) { + const data = this.data; + const el = this.el; + + const updatedData = AFRAME.utils.diff(oldData, data); + + for (const mapType of this.mapTypes) { + // create map function with name: Create + const createMapFunction = this[mapType + 'Create'].bind(this); + if (data.maps.includes(mapType) && !this[mapType]) { + // create Map element and save a link to it in this[mapType] + this[mapType] = createMapFunction(); + } else if (data.maps.includes(mapType) && (updatedData.longitude || updatedData.latitude || updatedData.elevation)) { + // call update map function with name: Update + this[mapType + 'Update'].bind(this)(); + } else if (this[mapType] && !data.maps.includes(mapType)) { + // remove element from DOM and from this object + this.el.removeChild(this[mapType]); + this[mapType] = null; + } + } + }, + mapbox2dCreate: function () { + const data = this.data; + const el = this.el; + + const mapbox2dElement = document.createElement('a-entity'); + mapbox2dElement.setAttribute('data-layer-name', 'Mapbox Satellite Streets'); + mapbox2dElement.setAttribute('geometry', 'primitive: plane; width: 512; height: 512;'); + mapbox2dElement.setAttribute('material', 'color: #ffffff; shader: flat; side: both; transparent: true;'); + //mapbox2dElement.setAttribute('position', '-7 -1 -2'); + mapbox2dElement.setAttribute('rotation', '-90 -4.25 0'); + mapbox2dElement.setAttribute('anisotropy', ''); + mapbox2dElement.setAttribute('mapbox', { + accessToken: MAPBOX_ACCESS_TOKEN_VALUE, + center: `${data.longitude}, ${data.latitude}`, + zoom: 15, + style: 'mapbox://styles/mapbox/satellite-streets-v11', + pxToWorldRatio: 4 + }); + mapbox2dElement.classList.add('autocreated'); + el.appendChild(mapbox2dElement); + return mapbox2dElement; + }, + google3dCreate: function () { + const data = this.data; + const el = this.el; + + const google3dElement = document.createElement('a-entity'); + google3dElement.setAttribute('data-layer-name', 'Google 3D Tiles'); + google3dElement.setAttribute('loader-3dtiles', { + url: 'https://tile.googleapis.com/v1/3dtiles/root.json', + long: data.longitude, + lat: data.latitude, + height: data.elevation - this.elevationHeightConstant, + googleApiKey: GOOGLE_API_KEY, + geoTransform: 'WGS84Cartesian', + maximumSSE: 48, + maximumMem: 400, + cameraEl: '#camera' + }); + google3dElement.classList.add('autocreated'); + el.appendChild(google3dElement); + return google3dElement; + }, + google3dUpdate: function () { + const data = this.data; + this.google3d.setAttribute('loader-3dtiles', { + lat: data.latitude, + long: data.longitude, + height: data.elevation - this.elevationHeightConstant, + }); + }, + mapbox2dUpdate: function () { + const data = this.data; + this.mapbox2d.setAttribute('mapbox', { + center: `${data.longitude}, ${data.latitude}` + }); + + } +}); diff --git a/src/index.js b/src/index.js index c75e20f29..0565f112f 100644 --- a/src/index.js +++ b/src/index.js @@ -16,6 +16,7 @@ require('./components/create-from-json'); require('./components/screentock.js'); require('aframe-atlas-uvs-component'); require('./components/streetplan-loader'); +require('./components/street-geo.js'); AFRAME.registerComponent('street', { schema: { diff --git a/test/aframe-streetmix-parsers-test.js b/test/aframe-streetmix-parsers.test.js similarity index 100% rename from test/aframe-streetmix-parsers-test.js rename to test/aframe-streetmix-parsers.test.js diff --git a/test/approvalTest/approval-tests.js b/test/approvalTest/approval-tests.test.js similarity index 100% rename from test/approvalTest/approval-tests.js rename to test/approvalTest/approval-tests.test.js diff --git a/test/browserTests/street-geo-test-bdd.mjs b/test/browserTests/street-geo-test-bdd.mjs new file mode 100644 index 000000000..2f6b6eff7 --- /dev/null +++ b/test/browserTests/street-geo-test-bdd.mjs @@ -0,0 +1,104 @@ +describe('street-geo component', function() { + let el; + + before((done) => { + const scene = document.createElement('a-scene'); + document.body.appendChild(scene); + el = document.createElement('a-entity'); + el.setAttribute('id', 'street-geo-test'); + el.setAttribute('street-geo', { + longitude: 10, + latitude: 20, + elevation: 30, + maps: 'mapbox2d' + }); + scene.appendChild(el); + + setTimeout(() => { + done(); + }, 500); + }); + + it('should create a mapbox2d element', () => { + const mapbox2dElement = el.querySelector('[data-layer-name="Mapbox Satellite Streets"]'); + expect(mapbox2dElement).to.exist; + }); + + it('should create a google3d element and delete mapbox2d element', (done) => { + const mapbox2dElement = el.querySelector('[data-layer-name="Mapbox Satellite Streets"]'); + const google3dElement = el.querySelector('[data-layer-name="Google 3D Tiles"]'); + + expect(mapbox2dElement).to.exist; + + el.setAttribute('street-geo', 'maps', 'google3d'); + + setTimeout(() => { + setTimeout(() => { + const updatedMapbox2dElement = el.querySelector('[data-layer-name="Mapbox Satellite Streets"]'); + const updatedGoogle3dElement = el.querySelector('[data-layer-name="Google 3D Tiles"]'); + + expect(updatedMapbox2dElement).to.not.exist; + expect(updatedGoogle3dElement).to.exist; + + done(); + }); + }); + }); + + it('should create both mapbox2d and google3d elements', (done) => { + el.setAttribute('street-geo', 'maps', 'mapbox2d, google3d'); + + setTimeout(() => { + setTimeout(() => { + const mapbox2dElement = el.querySelector('[data-layer-name="Mapbox Satellite Streets"]'); + const google3dElement = el.querySelector('[data-layer-name="Google 3D Tiles"]'); + expect(mapbox2dElement).to.exist; + expect(google3dElement).to.exist; + done(); + }); + }); + }); + + it('should delete mapbox2d and google3d elements after setting maps attribute to empty', (done) => { + const mapbox2dElement = el.querySelector('[data-layer-name="Mapbox Satellite Streets"]'); + const google3dElement = el.querySelector('[data-layer-name="Google 3D Tiles"]'); + + expect(mapbox2dElement).to.exist; + expect(google3dElement).to.exist; + + el.setAttribute('street-geo', 'maps', ''); + + setTimeout(() => { + setTimeout(() => { + const updatedMapbox2dElement = el.querySelector('[data-layer-name="Mapbox Satellite Streets"]'); + const updatedGoogle3dElement = el.querySelector('[data-layer-name="Google 3D Tiles"]'); + + expect(updatedMapbox2dElement).to.not.exist; + expect(updatedGoogle3dElement).to.not.exist; + + done(); + }); + }); + }); + + it('should update latitude, longitude, and elevation for google3d', (done) => { + el.setAttribute('street-geo', 'maps', 'google3d'); + el.setAttribute('street-geo', 'longitude', 40); + el.setAttribute('street-geo', 'latitude', 50); + el.setAttribute('street-geo', 'elevation', 100); + + setTimeout(() => { + setTimeout(() => { + const google3dElement = el.querySelector('[data-layer-name="Google 3D Tiles"]'); + expect(google3dElement).to.exist; + + const loader3dtilesAttr = google3dElement.getAttribute('loader-3dtiles'); + expect(loader3dtilesAttr.long).to.equal(40); + expect(loader3dtilesAttr.lat).to.equal(50); + expect(loader3dtilesAttr.height).to.equal(100 - 32.49158); + + done(); + }); + }); + }); +}); diff --git a/test/browserTests/street-geo-test.html b/test/browserTests/street-geo-test.html new file mode 100644 index 000000000..ec0a7499d --- /dev/null +++ b/test/browserTests/street-geo-test.html @@ -0,0 +1,26 @@ + + + + + Mocha Tests + + + +
+ + + + + + + + + + + + + diff --git a/test/create-from-json-utils-test.js b/test/create-from-json-utils.test.js similarity index 100% rename from test/create-from-json-utils-test.js rename to test/create-from-json-utils.test.js diff --git a/test/streetmix-utils-test.js b/test/streetmix-utils.test.js similarity index 100% rename from test/streetmix-utils-test.js rename to test/streetmix-utils.test.js