blob: 4cbeebe4f9715848fe15bf9d3043436a8af54e0a [file] [log] [blame]
/**
* @fileoverview Wrapper for constructing Google Maps instance.
*
* @license Copyright 2016 Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// Map requires google maps.
if (typeof google === 'undefined' || typeof google.maps === 'undefined') {
throw Error('Google Maps is required but missing.');
}
var geovelo;
geovelo = geovelo || {};
/**
* Construct the right kind of Google Map for the Geodetic Velocities
* visualization.
*
* @param {Element} containerElement The DOM element into which to insert.
*/
geovelo.Map = function(containerElement) {
// DOM Element into which to insert content.
this.domElement = document.createElement('div');
var style = this.domElement.style;
style.position = 'absolute';
style.top = style.bottom = style.right = style.left = 0;
if (containerElement) {
containerElement.appendChild(this.domElement);
}
// Callback handlers for when the bounds change.
this.boundsChangedHandlers = [];
// Initial coordinates.
var initial = {
zoom: 5,
lon: 139.7667,
lat: 35.6833
};
// Google Map Stylers.
var mapStyles = [
{
"stylers": [
{ "visibility": "off" }
]
},{
"featureType": "water",
"elementType": "geometry",
"stylers": [
{ "visibility": "on" },
{ "saturation": -50 }
]
},{
"featureType": "landscape.natural",
"stylers": [
{ "visibility": "on" },
{ "saturation": -100 },
{ "lightness": 100 }
]
},{
"featureType": "landscape",
"elementType": "labels",
"stylers": [
{ "visibility": "off" }
]
}
];
// Create the Google Map
var map = this.map = new google.maps.Map(this.domElement, {
zoom: 5,
center: new google.maps.LatLng(initial.lat, initial.lon),
mapTypeControl: false,
mapTypeId: google.maps.MapTypeId.TERRAIN,
streetViewControl: false,
styles: mapStyles
});
// Google Maps Overlay which is used to compute projection characteristics. We
// have to use the overlay projection's fromLatLngToContainerPixel() method
// rather than the map projection's fromLatLngToPoint() method because only
// the former returns the true screen coordinates relative to the map
// containing element's bounding rect.
var overlay = this.overlay = new google.maps.OverlayView();
overlay.onAdd = this.overlayAddHandler.bind(this);
overlay.draw = this.overlayDrawHandler.bind(this);
overlay.setMap(map);
// The overlay DOM element which we'll later attach.
this.overlayElement = null;
// Announce map boundary changes.
var emit = this.emitBoundsChanged.bind(this);
//map.addListener('bounds_changed', emit);
//map.addListener('center_changed', emit);
map.addListener('zoom_changed', emit);
map.addListener('idle', emit);
};
/**
* Add a bounds changed handler.
* @param {Function} handler Callback handler to invoke with new bounds.
*/
geovelo.Map.prototype.onBoundsChanged = function(handler) {
this.boundsChangedHandlers.push(handler);
};
/**
* Produce a custom 'bounds-changed' event to announce that the map bounds
* have been changed. This happens when the user zooms or pans the map.
*/
geovelo.Map.prototype.emitBoundsChanged = function() {
var center = this.map.getCenter();
var lon = geovelo.Map.normalizeLongitude(center.lng());
var lat = center.lat();
var bounds = this.map.getBounds();
var ne = bounds.getNorthEast();
var sw = bounds.getSouthWest();
var west = sw.lng();
var east = ne.lng();
// Using the west and east longitudinal values from SouthWest and NorthEast
// are fine when the viewport width only covers part of the Earth, but when
// sufficiently zoomed out, these values will erroneously report -180 and 180
// respectively. In that case, we have to compute the true east and west
// extent of the map.
var rect = this.domElement.getBoundingClientRect();
if (this.domElement.parentNode && rect.width) {
var lonPixelScale = this.getLonPixelScale();
var lonWidth = rect.width / 2 / lonPixelScale;
west = lon - lonWidth;
east = lon + lonWidth;
}
var detail = {
north: ne.lat(),
south: sw.lat(),
east: east,
west: west,
lon: lon,
lat: lat,
zoom: this.map.getZoom(),
width: rect.width || null,
height: rect.height || null,
left: rect.left,
right: rect.right,
top: rect.top,
bottom: rect.bottom
};
// Dispatch event and call change handlers.
this.domElement.dispatchEvent(
new CustomEvent('bounds-changed', {bubbles: true, detail: detail}));
for (var i = 0; i < this.boundsChangedHandlers.length; i++) {
this.boundsChangedHandlers[i].call(null, detail);
}
};
/**
* Given the current map characteristics, compute the longitudinal map scale.
* That is, how many screen pixels are there to one degree of latitude.
*/
geovelo.Map.prototype.getLonPixelScale = function() {
var LatLng = google.maps.LatLng;
var projection = this.overlay.getProjection();
var westMeridian = new LatLng(0, -90);
var eastMeridian = new LatLng(0, 90);
var westMeridianPixel = projection.fromLatLngToContainerPixel(westMeridian);
var eastMeridianPixel = projection.fromLatLngToContainerPixel(eastMeridian);
return Math.abs((westMeridianPixel.x - eastMeridianPixel.x) / 180);
};
/**
* Set the overlay DOM element.
*
* @param {HTMLElement} overlayElement The overlay's DOM element.
*/
geovelo.Map.prototype.setOverlayElement = function(overlayElement) {
this.overlayElement = overlayElement;
};
/**
* Handler for the overlay's onAdd method.
*/
geovelo.Map.prototype.overlayAddHandler = function() {
this.overlay.getPanes().overlayLayer.appendChild(this.overlayElement);
};
/**
* Handler for requests to draw the overlay.
*/
geovelo.Map.prototype.overlayDrawHandler = function() {
// Placeholder method to satisfy the Google Maps Overlay API.
};
/**
* Convenience method for normalizing a longitudinal coordinate to be between
* the bounds of -180 and 180.
*/
geovelo.Map.normalizeLongitude = function(lon) {
while (lon > 180) {
lon -= 360;
}
while (lon < -180) {
lon += 360;
}
return lon;
};