Initializing Google Maps as an AMD module - google-maps-api-3

To initialize google.maps as an AMD module, compliant with twitter/flight and requirejs, use:
define([
'components/flight/lib/component',
'async!http://maps.google.com/maps/api/js?key=AIzaSyDp9D9Db1CWfeGUJ1bin45s2WKZN5sapuM&sensor=false'
], function(defineComponent){
return defineComponent(newMap);
function newMap () {
this.defaultAttrs({
// Selector
mapDiv: '#map',
// Map Canvas
mapCanvas: {},
// Initialized?
initializedMap: false
});
this.initializeMap = function () {
var mapCenter = new google.maps.LatLng(39.960664,-75.605488);
var mapOptions = {
zoom: 15,
center: mapCenter,
disableDefaultUI: true,
mapTypeId: google.maps.MapTypeId.ROADMAP,
};
this.attr.mapCanvas = new google.maps.Map(document.getElementById("map"), mapOptions);
if (this.attr.mapCanvas != {} ) {
this.attr.initializedMap = true;
this.trigger(document, 'mapInitialized', {
status: this.attr.initializedMap
});
};
// ### Map events
//-----------
// Mouse Up
google.maps.event.addListener(this.attr.mapCanvas, 'mouseup', function() {
this.trigger('mouseup');
});
// Zoom Changed
google.maps.event.addListener(this.attr.mapCanvas, 'zoom_changed', function() {
this.trigger('zoomChanged');
});
};
this.mouseup = function () {
console.log("what");
}
this.zoomChanged = function () {
console.log("is up");
}
this.after('initialize', function () {
this.on('mouseup', this.mouseup);
this.on('zoomChanged', this.zoomChanged);
this.on('initializeMap', this.initializeMap);
this.trigger('initializeMap');
});
}
});

I put together a Google Maps AMD loader plugin, which adds some functionality on top of the async! loader.
require.config({
googlemaps: {
params: {
key: 'abcd1234', // sets api key
libraries: 'geometry' // set google libraries
}
}
});
require(['googlemaps!'], function(gmaps) {
// google.maps available as gmaps
var map = new gmaps.Map('map-canvas');
});

Related

How do I get all administrative divisions/boundaries along a route?

We're currently getting routes using the v8 endpoint seen at:
https://developer.here.com/documentation/routing-api/api-reference-swagger.html
For each route, we'd like to get all administrative divisions/regions/boundaries such as states, counties, cities, etc (for United States). How might we go about doing this?
We've thought about using HERE polylines in tandem with OpenStreetMap but I would hope that there might already be a solution for this?
you can use map tile api to to show map in the background as shown in this sample example : https://demo.support.here.com/examples/v3.1/simple_routing
(function(){
/*
author
(C) HERE 2019
*/
var mapContainer = document.getElementById('mapContainer');
// check if the site was loaded via secure connection
var secure = (location.protocol === 'https:') ? true : false;
var platform = new H.service.Platform({
useHTTPS: secure,
apikey: api_key
}),
defaultLayers = platform.createDefaultLayers(),
router = platform.getRoutingService(),
map = new H.Map(mapContainer, defaultLayers.vector.normal.map,
{
center: center,
zoom: zoom,
pixelRatio: window.devicePixelRatio || 1
}
);
// Do not draw under control panel
map.getViewPort().setPadding(0, 0, 0, $('.ctrl-panel').width());
// add behavior control
new H.mapevents.Behavior(new H.mapevents.MapEvents(map));
// Enable the default UI
var ui = H.ui.UI.createDefault(map, defaultLayers);
window.addEventListener('resize', function() { map.getViewPort().resize(); });
function calculateRoute()
{
var calculateRouteParams = {
'waypoint0' : '52.516222,13.388900',
'waypoint1' : '52.517175,13.395129',
'mode': 'fastest;car;traffic:disabled',
'representation': 'display'
},
onResult = function(result) {
var lineString = new H.geo.LineString(),
routeShape = result.response.route[0].shape,
polyline;
routeShape.forEach(function(point) {
var parts = point.split(',');
lineString.pushLatLngAlt(parts[0], parts[1]);
});
var polyline = new H.map.Polyline(lineString,
{
style:
{
lineWidth: 10,
strokeColor: "rgba(0, 128, 0, 0.7)"
}
});
map.addObject(polyline);
map.getViewModel().setLookAtData({
tilt: 45,
bounds: polyline.getBoundingBox()
});
},
onError = function(error) {
console.log(error);
}
router.calculateRoute(calculateRouteParams, onResult, onError);
}
var displayReady = function(e)
{
map.removeEventListener("mapviewchangeend", displayReady);
calculateRoute();
};
map.addEventListener("mapviewchangeend", displayReady);
})

Google Sites + Maps Javascript API + HTML5 Geolocation

I'm trying to implement a map with a route that has a waypoint and a dynamic origin within a Google Site. I'd like to set the origin based on the user's location who access the Google Sites and I'm using the HTML5 Geolocation API to retrieve the user's geolocation. However, I'm not able to retrieve properly the user's geolocation.
Here you will find the code I'm currently implementing.
<!DOCTYPE html>
<html>
<head>
<title>PATH A</title>
<script src="https://polyfill.io/v3/polyfill.min.js?features=default"></script>
<style type="text/css">
#map {
height: 100%;
}
/* Optional: Makes the sample page fill the window. */
html,
body {
height: 100%;
margin: 0;
padding: 0;
}
</style>
<script>
function initMap() {
const directionsService = new google.maps.DirectionsService();
const directionsRenderer = new google.maps.DirectionsRenderer();
const map = new google.maps.Map(document.getElementById("map"), {
zoom: 8,
center: { lat: 37.132, lng: 13.869 },
});
directionsRenderer.setMap(map);
const onChangeHandler = function () {
calculateAndDisplayRoute(directionsService, directionsRenderer);
};
window.onload = onChangeHandler;
}
function setPosition(position){
currentOrigin = {};
currentOrigin.lat = position.coords.latitude;
currentOrigin.lng = position.coord.longitude;
}
function calculateAndDisplayRoute(directionsService, directionsRenderer){
let currentOrigin = new google.maps.LatLng(37.081, 14.214);
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(setPosition);
} else {
// Browser doesn't support Geolocation
console.log("Browser does not support geolocation");
}
const waypts = [];
waypts.push({
location: { lat: 37.261, lng: 13.588},
stopover: true
});
directionsService.route({
origin: currentOrigin,
destination: {
lat: 37.314 , lng: 13.576
},
optimizeWaypoints: false,
waypoints: waypts,
travelMode: google.maps.TravelMode.DRIVING
}, (response, status) => {
if(status === "OK" && response) {
directionsRenderer.setDirections(response);
} else {
console.log("An error occurred: " + status);
}
});
}
</script>
</head>
<body>
<div id="map"></div>
<!-- Async script executes immediately and must be after any DOM elements used in callback. -->
<script
src="https://maps.googleapis.com/maps/api/js?key=*********&callback=initMap&libraries=&v=weekly"
async
></script>
</body>
</html>
Is the Geolocation API somehow blocked by the Google Sites' policies? If so, is there any other way to set up a dynamic origin? The desired solution is to have a map on the site that has a route with a specific waypoint.
Thanks in advance for your attention.
So a couple of things first I think you have a typo in your setPosition callback function the longitude should be position.coords.longitude . Also you need to set the center of the google maps object when the navigator returns a lat and lng. Right now currentOrigin is out of scope of the map and never is able to update. One thing you could possibly try is passing a reference of your map object to calculateAndDisplayRoute and setting the map's location in the setPosition callback.
Try this:
function initMap() {
const directionsService = new google.maps.DirectionsService();
const directionsRenderer = new google.maps.DirectionsRenderer();
const map = new google.maps.Map(document.getElementById("map"), {
zoom: 8,
center: { lat: 37.132, lng: 13.869 },
});
directionsRenderer.setMap(map);
const onChangeHandler = function () {
calculateAndDisplayRoute(directionsService, directionsRenderer, map);
};
window.onload = onChangeHandler;
}
function setPosition(position, map){
currentOrigin = {};
map.setCenter({ lat : position.coords.latitude, lng: position.coords.longitude });
}
function calculateAndDisplayRoute(directionsService, directionsRenderer, map){
let currentOrigin = new google.maps.LatLng(37.081, 14.214);
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(function (position) {
setPosition(position, map);
});
} else {
// Browser doesn't support Geolocation
console.log("Browser does not support geolocation");
}
const waypts = [];
waypts.push({
location: { lat: 37.261, lng: 13.588},
stopover: true
});
directionsService.route({
origin: currentOrigin,
destination: {
lat: 37.314 , lng: 13.576
},
optimizeWaypoints: false,
waypoints: waypts,
travelMode: google.maps.TravelMode.DRIVING
}, (response, status) => {
if(status === "OK" && response) {
directionsRenderer.setDirections(response);
} else {
console.log("An error occurred: " + status);
}
});
}
I passed the map object through to the callback so that setCenter could be called on the navigators position.

Change infowindow marker based on variable in realtime

How do I change the icon of a marker based on if a value is true or false.
I created an if function that checks the value of CameraStatus. I set it to false on default but the marker still won't change to RedStatus. It does change to RedStatus when I try a timer like this:
setTimeout(function () { MiamiMarker.setIcon(RedStatus) }, 10 * 1000);
It doesn't change to RedStatus when I try this:
var CameraStatus = false;
function CheckStatus() {
if (CameraStatus === false) {
MiamiMarker.SetIcon(RedStatus)
}
}
How do I change the marker based on my if function?
Eventually I want to change all my markers with boolean values I get from a home controller. The value of the boolean should decide if the marker has a GreenStats or RedStatus icon. First I'm trying to change one marker based on a hardcoded value. (See code below)
My code:
<script>
var map;
function initMap() {
var CenterLoc = { lat: 51.34, lng: 5.53 };
map = new google.maps.Map(document.getElementById('map'),
{
center: CenterLoc,
disableDefaultUI: true,
zoom: 3,
});
google.maps.event.addDomListener(window, 'load', initMap);
var Miami = { lat: 25.774266, lng: -80.193659 };
var MiamiMarker = new google.maps.Marker
({
position: Miami,
map: map,
icon: GreenStatus
});
//Replace standard google maps markers with colored dots
var GreenStatus = "#ViewBag.GreenStatus";
var OrangeStatus = "#ViewBag.OrangeStatus";
var RedStatus = "#ViewBag.RedStatus";
var CameraStatus = false;
function CheckStatus() {
if (CameraStatus === false) {
MiamiMarker.SetIcon(RedStatus)
}
}
var MiamiInfoCard = new google.maps.InfoWindow
({
content: '<div id="map-dialog"><h3>Miami</h3></div>'
});
MiamiMarker.addListener('click', function () {
MiamiInfoCard.open(map, MiamiMarker);
});
var position = new google.maps.LatLng(52.2305, 5.9924);
}
</script>
You can use an if else statement to change your Icon. Please note that it is .setIcon() and not *.SetIcon() just like in your code.
if (CameraStatus == false) {
MiamiMarker.setIcon(RedStatus)
} else {
MiamiMarker.setIcon(GreenStatus)
}
You can check this sample code that reproduces what you want in your use-case.
I used a toggle switch to set values for CameraStatus and call the CheckStatus() function passing the variables CameraStatus and MiamiMarker to be processed in the function.
var switchStatus = document.getElementById("mySwitch");
var CameraStatus;
switchStatus.addEventListener('change', function() {
if (switchStatus.checked) {
CameraStatus = false;
} else {
CameraStatus = true;
}
CheckStatus(CameraStatus, MiamiMarker);
});
I put the CheckStatus() function outside the initMap() function and passed the CameraStatus and MiamiMarker to change the marker's icon base on the value of CameraStatus.
function CheckStatus(CameraStatus, MiamiMarker) {
if (CameraStatus == false) {
MiamiMarker.setIcon(RedStatus)
} else {
MiamiMarker.setIcon(GreenStatus)
}
Hope this helps!

Google Maps API with backbone, how to bind an event

I've seen several other posts about this, but the answers from those responses don't work for me.
The other responses:
How do I bind a google maps geocoder.geocode() callback function
Backbone.js with Google Maps - problems with this and listeners
My code:
var ns = namespace('camelcase.geomanager.map');
ns.Site = Backbone.Model.extend({
url: '/site'
});
ns.Sites = Backbone.Collection.extend({
model: ns.Site
});
ns.MapView = Backbone.View.extend({
initialize: function() {
this.markers = new Array();
// Create the Google Map
var mapOptions = {
center: new google.maps.LatLng(-34.397, 150.644),
zoom: 8,
mapTypeId: google.maps.MapTypeId.ROADMAP
};
this.googleMap = new google.maps.Map(this.$(".mapCanvas")[0], mapOptions);
// Register events
this.collection.on('add', this.addSite, this);
this.collection.on('remove', this.removeSite, this);
},
addSite: function(model) {
// Get model attributes
var elementId = model.get('elementId');
var latitude = model.get('latitude');
var longitude = model.get('longitude');
var id = model.get('id');
var notes = model.get('notes');
var title = ""+id;
// Create icon and marker
var icon = '/resources/img/elements/' + elementId + '_marker.png';
var latLng = new google.maps.LatLng(latitude, longitude);
var marker = new google.maps.Marker({
position: latLng,
title: title,
map: this.googleMap,
icon: icon
});
// Load info window
var siteBubbleTemplate = _.template($('#siteBubbleTemplate').html());
var siteContent = $(siteBubbleTemplate({
siteId: id,
siteNotes: notes
}))[0];
var infoWindow = new google.maps.InfoWindow({
content: siteContent
});
// Show info window when clicking on marker
_.bindAll(this, this.openSite);
google.maps.event.addListener(marker, 'click', this.openSite(id));
this.markers.push({
id: id,
marker: marker,
infoWindow: infoWindow
});
},
openSite: function(id) {
var marker;
for (var c=0; c<this.markers.length; c++) {
marker = this.markers[c];
// Open the appropriate marker info window
if (marker.id == id) {
marker.infoWindow.open(googleMap, marker.marker);
}
// Close the rest
else {
marker.infoWindow.close();
}
}
}
});
The offending line:
google.maps.event.addListener(marker, 'click', this.openSite(id));
The error being reported in firebug:
TypeError: func is undefined
underscore.js (line 482)
I suspect this.marker is the problem, since you should be able to just refer to it by name.
google.maps.event.addListener(marker, 'click', this.openSite(id));
Looks like it was a scoping issue. I solved my problem with the following code:
// Show info window when clicking on marker
_.bindAll(this);
var _self = this;
var doSomething = function(event) {
_self.openSite({
event: event
});
};
google.maps.event.addListener(marker, 'click', doSomething);
I'll give the answer to whoever can best explain why this works.
In a model/collection event handler, Backbone sets 'this' to the model/collection which raised the event. If you call _.bindAll(this) in your view's initialize(), 'this' will be set to the view in your event handlers. Check out this jsfiddle: http://jsfiddle.net/9cvVv/339/ and see what happens when you uncomment _.bindAll(this);
var MyView = Backbone.View.extend({
initialize: function() {
// TODO: uncomment this line
// _.bindAll(this);
this.collection.bind('myEvent', this.onDoSomething);
},
updateCollection: function() {
this.collection.doSomething();
},
onDoSomething: function() {
if (this.models && typeof this.models.length === 'number') {
alert('"this" is a collection.');
}
else if (this.collection) {
alert('"this" is the view.');
}
else {
alert('"this" is something else.');
}
}
});

How to asynchronously load a google map in AngularJS?

Now that I have found a way to initialize Google Maps with the help of Andy Joslin in this SO initialize-google-map-in-angularjs, I am looking for a way to asynchronous load a Google Map Object.
I found an example of how to do this in the phonecat project.
Notice how the JS files are loaded in this example: index-async.html
In my Jade Scripts partial that is loaded into my program I tried:
script(src='js/lib/angular/angular.js')
script(src='js/lib/script/script.min.js')
script
$script([
'js/lib/angular/angular-resource.min.js',
'js/lib/jquery/jquery-1.7.2.min.js',
'http://maps.googleapis.com/maps/api/js?key=AIzaSyBTmi_pcXMZtLX5MWFRQgbVEYx-h-pDXO4&sensor=false',
'js/app.js',
'js/services.js',
'js/controllers.js',
'js/filters.js',
'js/directives.js',
'bootstrap/js/bootstrap.min.js'
], function() {
// when all is done, execute bootstrap angular application
angular.bootstrap(document, ['ofm']);
});
When I do this and go to load the map page I get:
A call to document.write() from an asycrononously-loaded
external script was ignored.
This is how Google Maps is being loaded now as a service:
'use strict';
var app = angular.module('ofm.services', []);
app.factory('GoogleMaps', function() {
var map_id = '#map';
var lat = 46.87916;
var lng = -3.32910;
var zoom = 15;
var map = initialize(map_id, lat, lng, zoom);
return map;
});
function initialize(map_id, lat, lng, zoom) {
var myOptions = {
zoom : 8,
center : new google.maps.LatLng(lat, lng),
mapTypeId : google.maps.MapTypeId.ROADMAP
};
return new google.maps.Map($(map_id)[0], myOptions);
}
It appears that this should be returning a promise from what I recall reading. But this AngularJS is very new to me.
here's my solution I came up without using jQuery:
(Gist here)
angular.module('testApp', []).
directive('lazyLoad', ['$window', '$q', function ($window, $q) {
function load_script() {
var s = document.createElement('script'); // use global document since Angular's $document is weak
s.src = 'https://maps.googleapis.com/maps/api/js?sensor=false&callback=initialize';
document.body.appendChild(s);
}
function lazyLoadApi(key) {
var deferred = $q.defer();
$window.initialize = function () {
deferred.resolve();
};
// thanks to Emil Stenström: http://friendlybit.com/js/lazy-loading-asyncronous-javascript/
if ($window.attachEvent) {
$window.attachEvent('onload', load_script);
} else {
$window.addEventListener('load', load_script, false);
}
return deferred.promise;
}
return {
restrict: 'E',
link: function (scope, element, attrs) { // function content is optional
// in this example, it shows how and when the promises are resolved
if ($window.google && $window.google.maps) {
console.log('gmaps already loaded');
} else {
lazyLoadApi().then(function () {
console.log('promise resolved');
if ($window.google && $window.google.maps) {
console.log('gmaps loaded');
} else {
console.log('gmaps not loaded');
}
}, function () {
console.log('promise rejected');
});
}
}
};
}]);
If you using jQuery in your AngularJS app, check out this function which returns a promise for when the Google Maps API has been loaded:
https://gist.github.com/gbakernet/828536
I was able to use this in a AngularJS directive to lazy-load Google Maps on demand.
Works a treat:
angular.module('mapModule') // usage: data-google-map
.directive('googleMap', ['$window', function ($window) {
return {
restrict: 'A',
link: function (scope, element, attrs) {
// If Google maps is already present then just initialise my map
if ($window.google && $window.google.maps) {
initGoogleMaps();
} else {
loadGoogleMapsAsync();
}
function loadGoogleMapsAsync() {
// loadGoogleMaps() == jQuery function from https://gist.github.com/gbakernet/828536
$.when(loadGoogleMaps())
// When Google maps is loaded, add InfoBox - this is optional
.then(function () {
$.ajax({ url: "/resources/js/infobox.min.js", dataType: "script", async: false });
})
.done(function () {
initGoogleMaps();
});
};
function initGoogleMaps() {
// Load your Google map stuff here
// Remember to wrap scope variables inside `scope.$apply(function(){...});`
}
}
};
}]);
Take a look of this i think its more reliable
var deferred = $q.defer();
var script = document.createElement('script');
$window.initMap = function() {
//console.log("Map init ");
deferred.resolve();
}
script.src = "//maps.googleapis.com/maps/api/js?v=3.exp&sensor=false&libraries=places&callback=initMap";
document.body.appendChild(script);
return deferred.promise;

Resources