Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support vector maps in google module #5981

Merged
merged 18 commits into from
Jul 22, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions examples/get-started/pure-js/google-maps/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,14 @@ with [webpack-dev-server](https://webpack.js.org/guides/development/#webpack-dev

## Usage

To run this example, you need a [Google Maps API key](https://developers.google.com/maps/documentation/javascript/get-api-key). You can either set an environment variable:
To run this example, you need a [Google Maps API key](https://developers.google.com/maps/documentation/javascript/get-api-key) and a [map id](https://developers.google.com/maps/documentation/javascript/webgl#vector-id). You can either set an environment variable:

```bash
export GoogleMapsAPIKey=<google_maps_api_key>
export GoogleMapsMapId=<google_maps_map_id>
```

Or set the `GOOGLE_MAPS_API_KEY` variable in `app.js`.
Or set the `GOOGLE_MAPS_API_KEY` and `GOOGLE_MAP_ID` variables in `app.js`.

To install dependencies:

Expand Down
6 changes: 4 additions & 2 deletions examples/get-started/pure-js/google-maps/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ const AIR_PORTS =

// Set your Google Maps API key here or via environment variable
const GOOGLE_MAPS_API_KEY = process.env.GoogleMapsAPIKey; // eslint-disable-line
const GOOGLE_MAPS_API_URL = `https://maps.googleapis.com/maps/api/js?key=${GOOGLE_MAPS_API_KEY}&libraries=visualization&v=3.45`;
const GOOGLE_MAP_ID = process.env.GoogleMapsMapId; // eslint-disable-line
const GOOGLE_MAPS_API_URL = `https://maps.googleapis.com/maps/api/js?key=${GOOGLE_MAPS_API_KEY}&v=beta&map_ids=${GOOGLE_MAP_ID}`;

function loadScript(url) {
const script = document.createElement('script');
Expand All @@ -24,7 +25,8 @@ function loadScript(url) {
loadScript(GOOGLE_MAPS_API_URL).then(() => {
const map = new google.maps.Map(document.getElementById('map'), {
center: {lat: 51.47, lng: 0.45},
zoom: 5
zoom: 5,
mapId: GOOGLE_MAP_ID
});

const overlay = new DeckOverlay({
Expand Down
6 changes: 3 additions & 3 deletions examples/get-started/pure-js/google-maps/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@
"build": "webpack -p"
},
"dependencies": {
"@deck.gl/core": "^8.1.0",
"@deck.gl/google-maps": "^8.1.0",
"@deck.gl/layers": "^8.1.0"
"@deck.gl/core": "^8.5.0",
"@deck.gl/google-maps": "^8.5.0",
"@deck.gl/layers": "^8.5.0"
},
"devDependencies": {
"webpack": "^4.20.2",
Expand Down
2 changes: 1 addition & 1 deletion examples/get-started/pure-js/google-maps/webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ const CONFIG = {

plugins: [
// Read google maps token from environment variable
new webpack.EnvironmentPlugin(['GoogleMapsAPIKey'])
new webpack.EnvironmentPlugin(['GoogleMapsAPIKey', 'GoogleMapsMapId'])
]
};

Expand Down
6 changes: 1 addition & 5 deletions modules/core/src/views/view.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,9 @@ export default class View {
height = '100%',

// Viewport Options
projectionMatrix = null, // Projection matrix
fovy = 50, // Perspective projection parameters, used if projectionMatrix not supplied
near = 0.1, // Distance of near clipping plane
far = 1000, // Distance of far clipping plane
modelMatrix = null, // A model matrix to be applied to position, to match the layer props API

// A View can be a wrapper for a viewport instance
viewportInstance = null,
Expand All @@ -38,11 +36,9 @@ export default class View {
this.props = {
...props,
id: this.id,
projectionMatrix,
fovy,
near,
far,
modelMatrix
far
};

// Extents
Expand Down
96 changes: 84 additions & 12 deletions modules/google-maps/src/google-maps-overlay.js
Original file line number Diff line number Diff line change
@@ -1,20 +1,25 @@
/* global google */
import {createDeckInstance, destroyDeckInstance, getViewState} from './utils';
import {setParameters, withParameters} from '@luma.gl/core';
import GL from '@luma.gl/constants';
import {
createDeckInstance,
destroyDeckInstance,
getViewPropsFromOverlay,
getViewPropsFromCoordinateTransformer
} from './utils';

const HIDE_ALL_LAYERS = () => false;
const GL_STATE = {
depthMask: true,
depthTest: true,
blendFunc: [GL.SRC_ALPHA, GL.ONE_MINUS_SRC_ALPHA, GL.ONE, GL.ONE_MINUS_SRC_ALPHA],
blendEquation: GL.FUNC_ADD
};

export default class GoogleMapsOverlay {
constructor(props) {
this.props = {};

this._map = null;

const overlay = new google.maps.OverlayView();
overlay.onAdd = this._onAdd.bind(this);
overlay.onRemove = this._onRemove.bind(this);
overlay.draw = this._draw.bind(this);
this._overlay = overlay;

this.setProps(props);
}

Expand All @@ -30,7 +35,9 @@ export default class GoogleMapsOverlay {
}
if (map) {
this._map = map;
this._overlay.setMap(map);
map.addListener('renderingtype_changed', () => {
this._createOverlay(map);
});
}
}

Expand Down Expand Up @@ -66,18 +73,56 @@ export default class GoogleMapsOverlay {
}

/* Private API */
_createOverlay(map) {
const {VECTOR, UNINITIALIZED} = google.maps.RenderingType;
const renderingType = map.getRenderingType();
if (renderingType === UNINITIALIZED) {
return;
}
const isVectorMap = renderingType === VECTOR;
const OverlayView = isVectorMap ? google.maps.WebglOverlayView : google.maps.OverlayView;
const overlay = new OverlayView();

// Lifecycle methods are different depending on map type
if (isVectorMap) {
overlay.onAdd = () => {};
overlay.onContextLost = this._onContextLost.bind(this);
overlay.onContextRestored = this._onContextRestored.bind(this);
overlay.onDraw = this._onDrawVector.bind(this);
} else {
overlay.onAdd = this._onAdd.bind(this);
overlay.draw = this._onDrawRaster.bind(this);
}
overlay.onRemove = this._onRemove.bind(this);
Comment on lines +88 to +96
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we define these methods in ES6 syntax? E.g.

const {VECTOR, UNINITIALIZED} = google.maps.RenderingType;

class GoogleMapsOverlay {
  _createOverlay(map) {
    this.renderingType = map.getRenderingType();
  }

  onAdd() {
    if (this.renderingType !== VECTOR) {
      this._deck = createDeckInstance(...);
    }
  }

  draw() {
    // old API - draw raster
  }

  onDraw() {
    // new API - draw vector
  }

  onRemove() {
  }
}

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I don't follow here. Are the methods _onAdd, _onContextRestored etc not already using the correct syntax? We need to bind them to the google.maps.WebglOverlayView object as per Google's docs


this._overlay = overlay;
this._overlay.setMap(map);
}

_onAdd() {
this._deck = createDeckInstance(this._map, this._overlay, this._deck, this.props);
}

_onContextRestored(gl) {
this._deck = createDeckInstance(this._map, this._overlay, this._deck, {gl, ...this.props});
}

_onContextLost() {
// TODO this isn't working
if (this._deck) {
destroyDeckInstance(this._deck);
this._deck = null;
}
}

_onRemove() {
// Clear deck canvas
this._deck.setProps({layerFilter: HIDE_ALL_LAYERS});
}

_draw() {
_onDrawRaster() {
const deck = this._deck;
const {width, height, left, top, zoom, pitch, latitude, longitude} = getViewState(
const {width, height, left, top, zoom, pitch, latitude, longitude} = getViewPropsFromOverlay(
this._map,
this._overlay
);
Expand All @@ -98,4 +143,31 @@ export default class GoogleMapsOverlay {
// Deck is initialized
deck.redraw();
}

// Vector code path
_onDrawVector(gl, coordinateTransformer) {
const deck = this._deck;

deck.setProps({
...getViewPropsFromCoordinateTransformer(this._map, coordinateTransformer)
});

if (deck.layerManager) {
this._overlay.requestRedraw();
withParameters(gl, GL_STATE, () => {
deck._drawLayers('google-vector', {
clearCanvas: false
});
});

// Reset state otherwise get rendering errors in
// Google library. These occur because the picking
// code is run outside of the _onDrawVector() method and
// the GL state can be inconsistent
setParameters(gl, {
scissor: [0, 0, gl.canvas.width, gl.canvas.height],
stencilFunc: [gl.ALWAYS, 0, 255, gl.ALWAYS, 0, 255]
});
}
}
}
80 changes: 73 additions & 7 deletions modules/google-maps/src/utils.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/* global google, document */
import {Deck} from '@deck.gl/core';
import {Matrix4} from 'math.gl';

// https://en.wikipedia.org/wiki/Web_Mercator_projection#Formulas
const MAX_LATITUDE = 85.05113;
Expand Down Expand Up @@ -57,7 +58,17 @@ function getContainer(overlay, style) {
const container = document.createElement('div');
container.style.position = 'absolute';
Object.assign(container.style, style);
overlay.getPanes().overlayLayer.appendChild(container);

// The DOM structure has a different structure depending on whether
// the Google map is rendered as vector or raster
if (overlay.getPanes) {
felixpalmer marked this conversation as resolved.
Show resolved Hide resolved
overlay.getPanes().overlayLayer.appendChild(container);
} else {
overlay
.getMap()
.getDiv()
.appendChild(container);
}
return container;
}

Expand All @@ -82,12 +93,8 @@ export function destroyDeckInstance(deck) {
* @param map (google.maps.Map) - The parent Map instance
* @param overlay (google.maps.OverlayView) - A maps Overlay instance
*/
export function getViewState(map, overlay) {
// The map fills the container div unless it's in fullscreen mode
// at which point the first child of the container is promoted
const container = map.getDiv().firstChild;
const width = container.offsetWidth;
const height = container.offsetHeight;
export function getViewPropsFromOverlay(map, overlay) {
const {width, height} = getMapSize(map);

// Canvas position relative to draggable map's container depends on
// overlayView's projection, not the map's. Have to use the center of the
Expand Down Expand Up @@ -145,8 +152,67 @@ export function getViewState(map, overlay) {
longitude
};
}

/* eslint-enable max-statements */

/**
* Get the current view state
* @param map (google.maps.Map) - The parent Map instance
* @param coordinateTransformer (google.maps.CoordinateTransformer) - A CoordinateTransformer instance
*/
export function getViewPropsFromCoordinateTransformer(map, coordinateTransformer) {
const {width, height} = getMapSize(map);
const {
lat: latitude,
lng: longitude,
heading: bearing,
tilt: pitch,
zoom
} = coordinateTransformer.getCameraParams();

// Match Google projection matrix
const fovy = 25;
const aspect = width / height;

// Match depth range (crucial for correct z-sorting)
const near = 0.75;
const far = 300000000000000;
// const far = Infinity;

const projectionMatrix = new Matrix4().perspective({
fovy: (fovy * Math.PI) / 180,
aspect,
near,
far
});
const focalDistance = 0.5 * projectionMatrix[5];

return {
width,
height,
viewState: {
altitude: focalDistance,
bearing,
latitude,
longitude,
pitch,
projectionMatrix,
repeat: true,
zoom: zoom - 1
}
};
}

function getMapSize(map) {
// The map fills the container div unless it's in fullscreen mode
// at which point the first child of the container is promoted
const container = map.getDiv().firstChild;
return {
width: container.offsetWidth,
height: container.offsetHeight
};
}

function getEventPixel(event, deck) {
if (event.pixel) {
return event.pixel;
Expand Down
Loading