This question already has answers here:
How to get a draggable waypoint's location from google directions result
(1 answer)
Google Maps API v3 - Directions with draggable alternate routes
(1 answer)
suppressMarkers and draggable for DirectionsRendererOptions Google Maps API
(1 answer)
How do I change the route provided to me by the directions API on web?
(1 answer)
Closed last month.
I am trying to create a polyline from the route. The route is draggable and it has waypoints. I am using the directions_changed event listener to draw the polyline so that whenever the route changes the polyline also changes. I am able to achieve all of this except then when I drag the route I get the new polyline but I also have the older polyline drawn on the route. Whenever the route is dragged I don't want the older polyline to appear along with the new polyline.
How can I achieve this?
function initMap() {
const map = new google.maps.Map(document.getElementById("map"), {
zoom: 4,
center: { lat: -24.345, lng: 134.46 }, // Australia.
});
const directionsService = new google.maps.DirectionsService();
const directionsRenderer = new google.maps.DirectionsRenderer({
draggable: true,
map,
panel: document.getElementById("panel"),
});
directionsRenderer.addListener("directions_changed", () => {
const directions = directionsRenderer.getDirections();
if (directions) {
computeTotalDistance(directions);
var polyline = new google.maps.Polyline(
{
path:google.maps.geometry.encoding.decodePath(directions.routes[0].overview_polyline),
map : map
}
)
if(polyline)
{
console.log(polyline)
polyline.setMap(map)
}
}
});
displayRoute(
"Perth, WA",
"Sydney, NSW",
directionsService,
directionsRenderer
);
}
function displayRoute(origin, destination, service, display) {
service
.route({
origin: origin,
destination: destination,
waypoints: [
{ location: "Adelaide, SA" },
{ location: "Broken Hill, NSW" },
],
travelMode: google.maps.TravelMode.DRIVING,
avoidTolls: true,
})
.then((result) => {
display.setDirections(result);
})
.catch((e) => {
alert("Could not display directions due to: " + e);
});
}
function computeTotalDistance(result) {
let total = 0;
const myroute = result.routes[0];
if (!myroute) {
return;
}
for (let i = 0; i < myroute.legs.length; i++) {
total += myroute.legs[i].distance.value;
}
total = total / 1000;
document.getElementById("total").innerHTML = total + " km";
}
window.initMap = initMap;
If you want to hide the old polyline, keep a reference to it (outside the scope of the directions_changed listener) and remove it from the map with polyline.setMap(null); before creating the new polyline:
if (polyline) {
// if polyline already exists, remove it from the map.
polyline.setMap(null)
}
polyline = new google.maps.Polyline({
path: google.maps.geometry.encoding.decodePath(directions.routes[0].overview_polyline),
map: map
})
proof of concept fiddle
code snippet:
function initMap() {
const map = new google.maps.Map(document.getElementById("map"), {
zoom: 4,
center: {
lat: -24.345,
lng: 134.46
}, // Australia.
});
const directionsService = new google.maps.DirectionsService();
const directionsRenderer = new google.maps.DirectionsRenderer({
draggable: true,
map,
panel: document.getElementById("panel"),
});
let polyline;
directionsRenderer.addListener("directions_changed", () => {
const directions = directionsRenderer.getDirections();
if (directions) {
computeTotalDistance(directions);
if (polyline) {
// if polyline already exists, remove it from the map.
polyline.setMap(null)
}
polyline = new google.maps.Polyline({
path: google.maps.geometry.encoding.decodePath(directions.routes[0].overview_polyline),
map: map
})
if (polyline) {
console.log(polyline)
polyline.setMap(map)
}
}
});
displayRoute(
"Perth, WA",
"Sydney, NSW",
directionsService,
directionsRenderer
);
}
function displayRoute(origin, destination, service, display) {
service
.route({
origin: origin,
destination: destination,
waypoints: [{
location: "Adelaide, SA"
},
{
location: "Broken Hill, NSW"
},
],
travelMode: google.maps.TravelMode.DRIVING,
avoidTolls: true,
})
.then((result) => {
display.setDirections(result);
})
.catch((e) => {
alert("Could not display directions due to: " + e);
});
}
function computeTotalDistance(result) {
let total = 0;
const myroute = result.routes[0];
if (!myroute) {
return;
}
for (let i = 0; i < myroute.legs.length; i++) {
total += myroute.legs[i].distance.value;
}
total = total / 1000;
document.getElementById("total").innerHTML = total + " km";
}
window.initMap = initMap;
/*
* Always set the map height explicitly to define the size of the div element
* that contains the map.
*/
#map {
height: 90%;
}
/*
* Optional: Makes the sample page fill the window.
*/
html,
body {
height: 100%;
margin: 0;
padding: 0;
}
#floating-panel {
position: absolute;
top: 10px;
left: 25%;
z-index: 5;
background-color: #fff;
padding: 5px;
border: 1px solid #999;
text-align: center;
font-family: "Roboto", "sans-serif";
line-height: 30px;
padding-left: 10px;
}
<!DOCTYPE html>
<html>
<head>
<title>Directions Service</title>
<script src="https://polyfill.io/v3/polyfill.min.js?features=default"></script>
<!-- jsFiddle will insert css and js -->
</head>
<body>
<div id="total"></div>
<div id="map"></div>
<!--
The `defer` attribute causes the callback to execute after the full HTML
document has been parsed. For non-blocking uses, avoiding race conditions,
and consistent behavior across browsers, consider loading using Promises
with https://www.npmjs.com/package/#googlemaps/js-api-loader.
-->
<script src="https://maps.googleapis.com/maps/api/js?key=AIzaSyCkUOdZ5y7hMm0yrcCQoCvLwzdM6M8s5qk&callback=initMap&v=weekly" defer></script>
</body>
</html>
I have the following code available for Google Maps which works with FusionTable Layer.
It changes based on the zoom level of the user.
The array draw_str and draw_str_zoom are dynamic and gets generated based on the API calls we make and the response we receive.
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="initial-scale=1.0">
<meta charset="utf-8">
<title>KML Layers</title>
<style>
/* Always set the map height explicitly to define the size of the div
* element that contains the map. */
#map {
height: 90%;
}
/* Optional: Makes the sample page fill the window. */
html, body {
height: 100%;
margin: 0;
padding: 0;
}
</style>
</head>
<body>
<div id="map"></div>
<script>
var layerState = [];
var draw_str = '27089,27013,27053,27029,27035,27067,27081,27069,27155,27153,27131,27123,27109,27051,27043,27031,27027,27007,27005,27003,27001';
var draw_str_zoom = '48131,48197,48459,48497,48163,48379,48403,48471,48499,48505,48067,48249,48289,48323,48351,48399,48467,48481,48485,48507,48031,48139,48187,48303,48315,48355,48409,48419,48429,48483,48489,48021,48027,48035,48053,48099,48143,48145,48157,48195,48225,48251,48271,48317,48319,48361,48427,48431,48463,48479,48043,48155,48215,48321,48343,48381,48425,48451,48469,48079,48135,48347,48363,48407,48477,48085,48201,48299,48325,48339,48413,48453,48503,48177,48291,48401,48423,48475,48089,48439,48095,48117,48253,48275,48329,48397,48441,48465,48259,48279,48335,48437,48487,48003,48045,48061,48121,48161,48209,48245,48281,48313,48375,48395,48445,48457,48473,48491,48501,48449,48435,48415,48405,48391,48387,48373,48371,48357,48349,48345,48337,48331,48309,48307,48293,48285,48277,48273,48265,48257,48255,48241,48231,48227,48223,48221,48219,48217,48213,48193,48189,48185,48183,48181,48175,48171,48167,48153,48151,48149,48147,48141,48133,48123,48119,48115,48113,48105,48097,48093,48091,48087,48077,48073,48071,48063,48059,48057,48055,48051,48049,48047,48041,48039,48037,48029,48025,48019,48015,48009,48005';
function initMap() {
var map = new google.maps.Map(document.getElementById('map'), {
zoom: 5,
center: {lat: 41.876, lng: -87.624}
});
layerState[0] = new google.maps.FusionTablesLayer({
query: {
select: '\'geometry\'',
from: '1fio1qgy5HkinUDKqYvREIlSoBaHjl2RBe3DLJa38'
},
options: {suppressInfoWindows: true},
styles: [{
polygonOptions: {
fillColor: '#000000',
fillOpacity: 0.001
}
}, {
where: "'GEO_ID2' IN (" + draw_str_zoom + ")",
polygonOptions: {
fillOpacity: 0.3
}
}]
});
layerState[0].setMap(map);
google.maps.event.addListener(map, 'zoom_changed', function () {
var zoom_level = map.getZoom();
if (zoom_level >= 6 && zoom_level < 7) {
clearRegion(zoom_level);
layerState[0] = new google.maps.FusionTablesLayer({
query: {
select: '\'geometry\'',
from: '1fio1qgy5HkinUDKqYvREIlSoBaHjl2RBe3DLJa38'
},
options: {suppressInfoWindows: true},
styles: [{
polygonOptions: {
fillColor: '#000000',
fillOpacity: 0.001
}
}, {
where: "'GEO_ID2' IN (" + draw_str + ")",
polygonOptions: {
fillOpacity: 0.3
}
}]
});
layerState[0].setMap(map);
}
});
function clearRegion(level) {
layerState.forEach(function(kml) {
kml.setMap(null);
});
}
}
// }
</script>
<script async defer
src="https://maps.googleapis.com/maps/api/js?key=AIzaSyBMtoh9P3UkoxbXndKu_HOP7KsVwTRvxGU&callback=initMap">
</script>
</body>
</html>
Google Fusion Table is going down - https://support.google.com/fusiontables/answer/9185417
Is there a way I could create this similar map with all the functionality without using FusionTable layer?
Since Fusion Tables will not be available after Dec. 3, 2019, I suggest:
Download your tables as KML files,
https://support.google.com/fusiontables/answer/2548807?hl=en
Host your KML files at a publicly accessible URL that does not require authentication to access. This is because Maps JavaScript API uses a Google hosted service to retrieve and parse KML files for rendering.
https://developers.google.com/maps/documentation/javascript/kmllayer
Finally, display the information of your KML files in a Google map and sidebar.
https://developers.google.com/maps/documentation/javascript/kml
Alternatively, you can host your KML files in the same domain where your webpage is, but for that you'll need a library like GeoXML3:
Examples:
http://www.dyasdesigns.com/geoxml/GeoXmlSamples.html
Repository that I used, EDIT 1 below:
https://github.com/geocodezip/geoxml3/tree/master/kmz
EDIT1: With GeoXML3, dynamically styling from draw_str would be:
<div id="map"></div>
<script src="geoxml3.js"></script>
<script>
var draw_str = [/* your array */];
function initMap() {
var map = new google.maps.Map(document.getElementById('map'), {
zoom: 5,
center: {lat: 41.876, lng: -87.624}
});
var myParser = new geoXML3.parser({map: map,zoom: false}); // zoom needs to be set to false to keep mapOptions intact.
myParser.parse('Counties.kml'); // our Document (myParser.docs[0]) is the only parsed kml
google.maps.event.addListener(map, 'zoom_changed', function () {
var polygonOptions = {fillColor: "#000000" , fillOpacity: 0.001};
var polygonOptionsID2 = {fillOpacity: 0.3};
var zoom_level = map.getZoom();
if (zoom_level >= 6 && zoom_level < 7) {
for (var i = 0; i < draw_str.length; i++) {
for (var j = 0; j < myParser.docs[0].placemarks.length; j++) {
if (draw_str[i] == myParser.docs[0].placemarks[j].vars.val.GEO_ID2) { // GEO_ID2 is stored as Extended Data for every placemark (myParser.docs[0].placemarks is an array with 3250 entries)
myParser.docs[0].gpolygons[j].setOptions(polygonOptionsID2); // myParser.docs[0].gpolygons is an array with 3250 polygons
} else {
myParser.docs[0].gpolygons[j].setOptions(polygonOptions);
}
}
}
}
});
}
</script>
It will be a bit slow since the KML downloaded from Fusion Tables has more than 15 MB
EDIT2: fully functional demo consuming both draw_str and draw_str_zoom, dependent on having both GeoXML3.js and the KML downloaded from the Fusion Table page, referenced in the code.
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="initial-scale=1.0">
<meta charset="utf-8">
<title>KML Layers</title>
<style>
#map {
height: 100%;
}
html, body {
height: 100%;
margin: 0;
padding: 0;
}
</style>
</head>
<body>
<div id="map"></div>
<script src="static/geoxml3.js"></script>
<script>
var draw_str = "27089,27013,27053,27029,27035,27067,27081,27069,27155,27153,27131,27123,27109,27051,27043,27031,27027,27007,27005,27003,27001";
var draw_str_zoom = "48131,48197,48459,48497,48163,48379,48403,48471,48499,48505,48067,48249,48289,48323,48351,48399,48467,48481,48485,48507,48031,48139,48187,48303,48315,48355,48409,48419,48429,48483,48489,48021,48027,48035,48053,48099,48143,48145,48157,48195,48225,48251,48271,48317,48319,48361,48427,48431,48463,48479,48043,48155,48215,48321,48343,48381,48425,48451,48469,48079,48135,48347,48363,48407,48477,48085,48201,48299,48325,48339,48413,48453,48503,48177,48291,48401,48423,48475,48089,48439,48095,48117,48253,48275,48329,48397,48441,48465,48259,48279,48335,48437,48487,48003,48045,48061,48121,48161,48209,48245,48281,48313,48375,48395,48445,48457,48473,48491,48501,48449,48435,48415,48405,48391,48387,48373,48371,48357,48349,48345,48337,48331,48309,48307,48293,48285,48277,48273,48265,48257,48255,48241,48231,48227,48223,48221,48219,48217,48213,48193,48189,48185,48183,48181,48175,48171,48167,48153,48151,48149,48147,48141,48133,48123,48119,48115,48113,48105,48097,48093,48091,48087,48077,48073,48071,48063,48059,48057,48055,48051,48049,48047,48041,48039,48037,48029,48025,48019,48015,48009,48005";
var draw_strArray = draw_str.replace(/, +/g, ",").split(",").map(Number);
var zoomArray = draw_str_zoom.replace(/, +/g, ",").split(",").map(Number);
var tempOptions = {fillColor: "#FFFF00", strokeColor: "#000000", fillOpacity: 0.9, strokeWidth: 10};
var geoXmlDoc;
function initMap() {
var map = new google.maps.Map(document.getElementById('map'), {
zoom: 5,
center: {lat: 41.876, lng: -87.624}
});
var myParser = new geoXML3.parser({map: map,zoom: false, afterParse: useTheData });
myParser.parse('static/Counties.kml');
google.maps.event.addListener(map, 'zoom_changed', function () {
var zoom_level = map.getZoom();
if (zoom_level >= 6 && zoom_level < 7) {
for ( i = 0; i < draw_strArray.length; i++) {
for (var j = 0; j < geoXmlDoc.placemarks.length; j++) {
if (draw_strArray[i] == geoXmlDoc.placemarks[j].vars.val.GEO_ID2) {
geoXmlDoc.gpolygons[j].setOptions(tempOptions);
}
}
}
} else {
for ( i = 0; i < draw_strArray.length; i++) {
for (var j = 0; j < geoXmlDoc.placemarks.length; j++) {
if (draw_strArray[i] == geoXmlDoc.placemarks[j].vars.val.GEO_ID2) {
geoXmlDoc.gpolygons[j].setOptions({fillOpacity: 0.0});
}
}
}
}
});
function useTheData(doc) {
geoXmlDoc= doc[0];
for ( i = 0; i < zoomArray.length; i++) {
for (var j = 0; j < geoXmlDoc.placemarks.length; j++) {
if (zoomArray[i] == geoXmlDoc.placemarks[j].vars.val.GEO_ID2) {
geoXmlDoc.gpolygons[j].setOptions(tempOptions);
}
}
}
};
}
</script>
<script async defer
src="https://maps.googleapis.com/maps/api/js?key=AIzaSyBMtoh9P3UkoxbXndKu_HOP7KsVwTRvxGU&callback=initMap">
</script>
</body>
</html>
I am currently using Cytoscape JS to visualize a network but I would like to overlay the graph on a world map and plot the nodes to specific locations. The Tokyo Railways Demo is similar to what I have imagined but the background is black. I want my map to look like the google maps and have similar zoom in and out capabilities. It would be great if there is a way to integrate the google maps in the Cytoscape graph.
Cytoscape.js on Google Maps, a demo code based on a sample overlay-simple. The current state is rough, but it works.
Try running the code below in full screen. To zoom IN/OUT, use press_Ctrl + scroll_wheel.
The best platform to test this code is jsfiddle.net, by forking the original code and replacing parts with the code presented here.
/*
Status: google_APIkey is good for development only
*/
// This example creates a custom overlay called CyOverlay, containing
// a U.S. Geological Survey (USGS) image of the relevant area on the map.
// Set the custom overlay object's prototype to a new instance
// of OverlayView. In effect, this will subclass the overlay class therefore
// it's simpler to load the API synchronously, using
// google.maps.event.addDomListener().
// Note that we set the prototype to an instance, rather than the
// parent class itself, because we do not wish to modify the parent class.
// some data here
var cyto_divid = 'cy'; //outside, below gmaps
var goverlay_id = 'cy4'; //overlay on gmaps
var timeoutsec = 500; //millisec
var cy;
var base_dc = 0.03; //TODO: derive a formula
var lon_ext = 25; //extent in lng
var lonmin = 90.0; //90
var latmin = 0; //0
var lon_cor = lon_ext*base_dc;
var lat_ext = lon_ext; //extent in lat
var lonmax = lonmin + lon_ext;
var lonmid = (lonmin+lonmax)/2;
var lonmax2 = lonmax + lon_cor;
var latmax = latmin + lat_ext;
var latmid = (latmin+latmax)/2;
var latlng = { lat: latmin, lng: lonmin};
var latlng2 = { lat: latmax, lng: lonmax2};
var clatlng = { lat: latmid, lng: lonmid};
var cbottom = { lat: latmin, lng: lonmid};
function lnglat2xy(lon, lat) {
let w = 500;
let h = 500;
let L = lonmax2-lonmin;
let B = latmax-latmin;
let y = (B-(lat-latmin))*h/B;
let x = (lon-lonmin)*w/L;
return {x: x, y: y};
}
var bkk = lnglat2xy(100.494, 13.75);
var bda = lnglat2xy(95.502586, 5.412598);
var hoc = lnglat2xy(106.729, 10.747);
var han = lnglat2xy(105.90673, 21.03176);
var chi = lnglat2xy(99.837, 19.899);
var nay = lnglat2xy(96.116, 19.748);
var overlay;
CyOverlay.prototype = new google.maps.OverlayView();
// Initialize the map and the custom overlay.
function initMap() {
var map = new google.maps.Map(document.getElementById('map'), {
zoom: 5, //11,
center: {
lat: (latmax+latmin)/2, //62.323907,
lng: (lonmax2+lonmin)/2 //-150.109291
},
mapTypeId: 'roadmap' //
});
map.addListener('zoom_changed', function() {
setTimeout(function delay() {
cy.fit(cy.$('#LL, #UR'));
}, timeoutsec)
});
var bounds = new google.maps.LatLngBounds(
new google.maps.LatLng(latmin, lonmin), //62.281819, -150.287132)
new google.maps.LatLng(latmax, lonmax2)); //62.400471, -150.005608))
// The photograph is courtesy of the U.S. Geological Survey.
var srcImage;
/*srcImage = 'https://developers.google.com/maps/documentation/' +
'javascript/examples/full/images/talkeetna.png';*/
// The custom CyOverlay object contains the USGS image,
// the bounds of the image, and a reference to the map.
overlay = new CyOverlay(bounds, srcImage, map);
// init cytoscape
setTimeout(function delay() {
cyto_run();
}, timeoutsec)
}
/** #constructor */
function CyOverlay(bounds, image, map) {
// Initialize all properties.
this.bounds_ = bounds;
this.image_ = image;
this.map_ = map;
// Define a property to hold the image's div. We'll
// actually create this div upon receipt of the onAdd()
// method so we'll leave it null for now.
this.div_ = null;
// Explicitly call setMap on this overlay.
this.setMap(map);
}
/**
* onAdd is called when the map's panes are ready and the overlay has been
* added to the map.
*/
CyOverlay.prototype.onAdd = function() {
var div = document.createElement('div');
div.id = goverlay_id;
div.style.borderStyle = 'dashed';
//div.style.backgroundColor = rgba(76,76,76,0.25);
div.style.opacity = 0.8;
div.style.borderWidth = '1px';
div.style.borderColor = 'gray';
div.style.position = 'absolute';
// Create the img element and attach it to the div.
var img = document.createElement('img');
img.src = this.image_;
img.style.width = '100%';
img.style.height = '100%';
img.style.position = 'absolute';
//div.appendChild(img); //ignore overlay img
this.div_ = div;
// Add the element to the "overlayLayer" pane.
var panes = this.getPanes();
panes.overlayLayer.appendChild(div);
};
CyOverlay.prototype.draw = function() {
// We use the south-west and north-east
// coordinates of the overlay to peg it to the correct position and size.
// To do this, we need to retrieve the projection from the overlay.
var overlayProjection = this.getProjection();
// Retrieve the south-west and north-east coordinates of this overlay
// in LatLngs and convert them to pixel coordinates.
// We'll use these coordinates to resize the div.
var sw = overlayProjection.fromLatLngToDivPixel(this.bounds_.getSouthWest());
var ne = overlayProjection.fromLatLngToDivPixel(this.bounds_.getNorthEast());
// Resize the image's div to fit the indicated dimensions.
var div = this.div_;
div.style.left = sw.x + 'px';
div.style.top = ne.y + 'px';
div.style.width = (ne.x - sw.x) + 'px';
div.style.height = (sw.y - ne.y) + 'px';
/*
cytoscape needs regen here
*/
setTimeout(function delay() {
cy.fit(cy.$('#LL, #UR'));
}, timeoutsec)
};
// The onRemove() method will be called automatically from the API if
// we ever set the overlay's map property to 'null'.
CyOverlay.prototype.onRemove = function() {
this.div_.parentNode.removeChild(this.div_);
this.div_ = null;
};
google.maps.event.addDomListener(window, 'load', initMap);
/* ___________cytoscape.js code____________ */
function cyto_run() {
cy = cytoscape({
container: document.getElementById(goverlay_id),
elements: {
nodes: [
{data: {id: "LL", nname: "LowerLeft"},
classes: "controlpoint",
position: {x: 0, y: 500}},
{data: {id: "UL", nname: "UpperLeft"},
classes: "controlpoint",
position: {x: 0, y: 0}},
{data: {id: "UR", nname: "UpperRight"},
classes: "controlpoint",
position: {x: 500, y: 0}},
{data: {id: "LR", nname: "LowerRight"},
classes: "controlpoint",
position: {x: 500, y: 500}},
{data: {id: "bkk", name: "Bangkok"}, classes: "datapoint", position: {x: bkk.x, y: bkk.y}},
{data: {id: "bda", name: "Banda"}, classes: "datapoint", position: {x: bda.x, y: bda.y}},
{data: {id: "hoc", name: "Hochi"}, classes: "datapoint", position: {x: hoc.x, y: hoc.y}},
{data: {id: "han", name: "Hanoi"}, classes: "datapoint", position: {x: han.x, y: han.y}},
{data: {id: "nay", name: "Nay"}, classes: "datapoint", position: {x: nay.x, y: nay.y}},
],
edges: [
{data: {source: "nay", target: "bda", label: "NAB"},
classes: 'autorotate'},
{data: {source: "nay", target: "han", label: "NAH"},
classes: 'autorotate'},
{data: {source: "bda", target: "bkk", label: "dgfh"}},
{data: {source: "hoc", target: "bkk", label: "tert"}},
{data: {source: "hoc", target: "bda", label: "HOB"},
classes: 'autorotate'},
{data: {source: "hoc", target: "han", label: "HOH"},
classes: 'autorotate'},
{data: {source: "han", target: "bkk", label: "terf"}},
{data: {id: "LLUR" , source: "LL", target: "UR"},
classes: "controlline"},
{data: {id: "ULLR" , source: "UL", target: "LR"},
classes: "controlline"},
]
},
style: [
{
selector: "node.datapoint",
style: {
shape: "ellipse",
width: '12px',
height: '12px',
"background-color": "blue",
label: "data(name)",
opacity: 0.85,
"text-background-color": "yellow"
}
},
{
selector: "node.controlpoint",
style: {
shape: "triangle",
width: '4px',
height: '4px',
"background-color": "blue",
label: "data(id)",
opacity: 0.85,
}
},
{
selector: "edge[label]",
style: {
label: "data(label)",
width: 2,
"line-color": "#909",
"target-arrow-color": "#f00",
"curve-style": "bezier",
"target-arrow-shape": "vee",
//"target-arrow-fill": "red",
"arrow-scale": 1.0,
}
},
{
"selector": ".autorotate",
"style": {
"edge-text-rotation": "autorotate"
}
},
{
"selector": ".controlline",
"style": {
width: "2px",
"line-color": "green",
opacity: 0.35
}
},
{
selector: ".highlight",
css: {
"background-color": "yellow"
}
}
],
layout: {
name: "preset"
}
});
}
/* Always set the map height explicitly to define the size of the div
* element that contains the map. */
#map {
height: 90%;
}
/* Optional: Makes the sample page fill the window. */
html,
body {
height: 100%;
margin: 0;
padding: 0;
}
#cy {
width: 600px;
height: 600px;
position: absolute;
}
#cy4 {
width: 600px;
height: 600px;
position: absolute;
}
<b>Cytoscape on Google Maps</b>
<div id="map"></div>
<div id="cy"></div>
<!-- Replace the value of the key parameter with your own API key. -->
<script src="https://maps.googleapis.com/maps/api/js?key=AIzaSyCkUOdZ5y7hMm0yrcCQoCvLwzdM6M8s5qk"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/cytoscape/3.9.2/cytoscape.min.js"></script>
There is a plugin cytoscape-mapbox-gl, which intergrates Cytoscape.js and Mapbox GL JS. It can be used with any raster or vector basemap layer.
I'm the author.
I am trying to get the Snap to Road demo working that is supplied here but I keep getting the error: Google Maps API error: MissingKeyMapError. I already searched around and this is the error Google gives you for either supplying no key or a invalid key but even if I generate a brand new key it won't work. It just gives me a blank screen.
Why is it throwing this error even though I supply the code with the API key?
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="initial-scale=1.0, user-scalable=no">
<meta charset="utf-8">
<title>Roads API Demo</title>
<style>
html,
body,
#map {
height: 100%;
margin: 0px;
padding: 0px
}
#panel {
position: absolute;
top: 5px;
left: 50%;
margin-left: -180px;
z-index: 5;
background-color: #fff;
padding: 5px;
border: 1px solid #999;
}
#bar {
width: 240px;
background-color: rgba(255, 255, 255, 0.75);
margin: 8px;
padding: 4px;
border-radius: 4px;
}
#autoc {
width: 100%;
box-sizing: border-box;
}
</style>
<script src="jquery-3.2.1.js"></script>
<script src="https://maps.googleapis.com/maps/api/js?libraries=drawing,places"></script>
<script>
var apiKey = 'AIzaSyB3kpx3fgR9VT3WL2tp49QrbnyDdgygGeo';
var map;
var drawingManager;
var placeIdArray = [];
var polylines = [];
var snappedCoordinates = [];
function initialize() {
var mapOptions = {
zoom: 17,
center: {
lat: -33.8667,
lng: 151.1955
}
};
map = new google.maps.Map(document.getElementById('map'), mapOptions);
// Adds a Places search box. Searching for a place will center the map on that
// location.
map.controls[google.maps.ControlPosition.RIGHT_TOP].push(
document.getElementById('bar'));
var autocomplete = new google.maps.places.Autocomplete(
document.getElementById('autoc'));
autocomplete.bindTo('bounds', map);
autocomplete.addListener('place_changed', function() {
var place = autocomplete.getPlace();
if (place.geometry.viewport) {
map.fitBounds(place.geometry.viewport);
} else {
map.setCenter(place.geometry.location);
map.setZoom(17);
}
});
// Enables the polyline drawing control. Click on the map to start drawing a
// polyline. Each click will add a new vertice. Double-click to stop drawing.
drawingManager = new google.maps.drawing.DrawingManager({
drawingMode: google.maps.drawing.OverlayType.POLYLINE,
drawingControl: true,
drawingControlOptions: {
position: google.maps.ControlPosition.TOP_CENTER,
drawingModes: [
google.maps.drawing.OverlayType.POLYLINE
]
},
polylineOptions: {
strokeColor: '#696969',
strokeWeight: 2
}
});
drawingManager.setMap(map);
// Snap-to-road when the polyline is completed.
drawingManager.addListener('polylinecomplete', function(poly) {
var path = poly.getPath();
polylines.push(poly);
placeIdArray = [];
runSnapToRoad(path);
});
// Clear button. Click to remove all polylines.
$('#clear').click(function(ev) {
for (var i = 0; i < polylines.length; ++i) {
polylines[i].setMap(null);
}
polylines = [];
ev.preventDefault();
return false;
});
}
// Snap a user-created polyline to roads and draw the snapped path
function runSnapToRoad(path) {
var pathValues = [];
for (var i = 0; i < path.getLength(); i++) {
pathValues.push(path.getAt(i).toUrlValue());
}
$.get('https://roads.googleapis.com/v1/snapToRoads', {
interpolate: true,
key: apiKey,
path: pathValues.join('|')
}, function(data) {
processSnapToRoadResponse(data);
drawSnappedPolyline();
getAndDrawSpeedLimits();
});
}
// Store snapped polyline returned by the snap-to-road service.
function processSnapToRoadResponse(data) {
snappedCoordinates = [];
placeIdArray = [];
for (var i = 0; i < data.snappedPoints.length; i++) {
var latlng = new google.maps.LatLng(
data.snappedPoints[i].location.latitude,
data.snappedPoints[i].location.longitude);
snappedCoordinates.push(latlng);
placeIdArray.push(data.snappedPoints[i].placeId);
}
}
// Draws the snapped polyline (after processing snap-to-road response).
function drawSnappedPolyline() {
var snappedPolyline = new google.maps.Polyline({
path: snappedCoordinates,
strokeColor: 'black',
strokeWeight: 3
});
snappedPolyline.setMap(map);
polylines.push(snappedPolyline);
}
// Gets speed limits (for 100 segments at a time) and draws a polyline
// color-coded by speed limit. Must be called after processing snap-to-road
// response.
function getAndDrawSpeedLimits() {
for (var i = 0; i <= placeIdArray.length / 100; i++) {
// Ensure that no query exceeds the max 100 placeID limit.
var start = i * 100;
var end = Math.min((i + 1) * 100 - 1, placeIdArray.length);
drawSpeedLimits(start, end);
}
}
// Gets speed limits for a 100-segment path and draws a polyline color-coded by
// speed limit. Must be called after processing snap-to-road response.
function drawSpeedLimits(start, end) {
var placeIdQuery = '';
for (var i = start; i < end; i++) {
placeIdQuery += '&placeId=' + placeIdArray[i];
}
$.get('https://roads.googleapis.com/v1/speedLimits',
'key=' + apiKey + placeIdQuery,
function(speedData) {
processSpeedLimitResponse(speedData, start);
}
);
}
// Draw a polyline segment (up to 100 road segments) color-coded by speed limit.
function processSpeedLimitResponse(speedData, start) {
var end = start + speedData.speedLimits.length;
for (var i = 0; i < speedData.speedLimits.length - 1; i++) {
var speedLimit = speedData.speedLimits[i].speedLimit;
var color = getColorForSpeed(speedLimit);
// Take two points for a single-segment polyline.
var coords = snappedCoordinates.slice(start + i, start + i + 2);
var snappedPolyline = new google.maps.Polyline({
path: coords,
strokeColor: color,
strokeWeight: 6
});
snappedPolyline.setMap(map);
polylines.push(snappedPolyline);
}
}
function getColorForSpeed(speed_kph) {
if (speed_kph <= 40) {
return 'purple';
}
if (speed_kph <= 50) {
return 'blue';
}
if (speed_kph <= 60) {
return 'green';
}
if (speed_kph <= 80) {
return 'yellow';
}
if (speed_kph <= 100) {
return 'orange';
}
return 'red';
}
$(window).on('load', function() {
initialize
});
</script>
</head>
<body>
<div id="map"></div>
<div id="bar">
<p class="auto"><input type="text" id="autoc" /></p>
<p><a id="clear" href="#">Click here</a> to clear map.</p>
</div>
</body>
</html>
If you haven't got an API KEY:
The script element that loads the API is missing the required authentication parameter. If you are using the standard Maps JavaScript API, you must use a key parameter with a valid API key.
Step 1: Get a Key from this URL
https://developers.google.com/maps/documentation/javascript/get-api-key
Step 2: Include your script with the API KEY
<script async defer src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&callback=initMap"
type="text/javascript"></script>
Or replace
var apiKey = 'YOUR_API_KEY';
If you got an API KEY
Google maps Snap to Road demo
Full Code
html, body, #map {
height: 100%;
margin: 0px;
padding: 0px
}
#panel {
position: absolute;
top: 5px;
left: 50%;
margin-left: -180px;
z-index: 5;
background-color: #fff;
padding: 5px;
border: 1px solid #999;
}
#bar {
width: 240px;
background-color: rgba(255, 255, 255, 0.75);
margin: 8px;
padding: 4px;
border-radius: 4px;
}
#autoc {
width: 100%;
box-sizing: border-box;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="initial-scale=1.0, user-scalable=no">
<meta charset="utf-8">
<title>Roads API Demo</title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="/_static/js/jquery-bundle.js"></script>
<script
src="https://maps.googleapis.com/maps/api/js?libraries=drawing,places"></script>
<script>
var apiKey = 'AIzaSyB3kpx3fgR9VT3WL2tp49QrbnyDdgygGeo';
var map;
var drawingManager;
var placeIdArray = [];
var polylines = [];
var snappedCoordinates = [];
function initialize() {
var mapOptions = {
zoom: 17,
center: {lat: -33.8667, lng: 151.1955}
};
map = new google.maps.Map(document.getElementById('map'), mapOptions);
// Adds a Places search box. Searching for a place will center the map on that
// location.
map.controls[google.maps.ControlPosition.RIGHT_TOP].push(
document.getElementById('bar'));
var autocomplete = new google.maps.places.Autocomplete(
document.getElementById('autoc'));
autocomplete.bindTo('bounds', map);
autocomplete.addListener('place_changed', function() {
var place = autocomplete.getPlace();
if (place.geometry.viewport) {
map.fitBounds(place.geometry.viewport);
} else {
map.setCenter(place.geometry.location);
map.setZoom(17);
}
});
// Enables the polyline drawing control. Click on the map to start drawing a
// polyline. Each click will add a new vertice. Double-click to stop drawing.
drawingManager = new google.maps.drawing.DrawingManager({
drawingMode: google.maps.drawing.OverlayType.POLYLINE,
drawingControl: true,
drawingControlOptions: {
position: google.maps.ControlPosition.TOP_CENTER,
drawingModes: [
google.maps.drawing.OverlayType.POLYLINE
]
},
polylineOptions: {
strokeColor: '#696969',
strokeWeight: 2
}
});
drawingManager.setMap(map);
// Snap-to-road when the polyline is completed.
drawingManager.addListener('polylinecomplete', function(poly) {
var path = poly.getPath();
polylines.push(poly);
placeIdArray = [];
runSnapToRoad(path);
});
// Clear button. Click to remove all polylines.
$('#clear').click(function(ev) {
for (var i = 0; i < polylines.length; ++i) {
polylines[i].setMap(null);
}
polylines = [];
ev.preventDefault();
return false;
});
}
// Snap a user-created polyline to roads and draw the snapped path
function runSnapToRoad(path) {
var pathValues = [];
for (var i = 0; i < path.getLength(); i++) {
pathValues.push(path.getAt(i).toUrlValue());
}
$.get('https://roads.googleapis.com/v1/snapToRoads', {
interpolate: true,
key: apiKey,
path: pathValues.join('|')
}, function(data) {
processSnapToRoadResponse(data);
drawSnappedPolyline();
getAndDrawSpeedLimits();
});
}
// Store snapped polyline returned by the snap-to-road service.
function processSnapToRoadResponse(data) {
snappedCoordinates = [];
placeIdArray = [];
for (var i = 0; i < data.snappedPoints.length; i++) {
var latlng = new google.maps.LatLng(
data.snappedPoints[i].location.latitude,
data.snappedPoints[i].location.longitude);
snappedCoordinates.push(latlng);
placeIdArray.push(data.snappedPoints[i].placeId);
}
}
// Draws the snapped polyline (after processing snap-to-road response).
function drawSnappedPolyline() {
var snappedPolyline = new google.maps.Polyline({
path: snappedCoordinates,
strokeColor: 'black',
strokeWeight: 3
});
snappedPolyline.setMap(map);
polylines.push(snappedPolyline);
}
// Gets speed limits (for 100 segments at a time) and draws a polyline
// color-coded by speed limit. Must be called after processing snap-to-road
// response.
function getAndDrawSpeedLimits() {
for (var i = 0; i <= placeIdArray.length / 100; i++) {
// Ensure that no query exceeds the max 100 placeID limit.
var start = i * 100;
var end = Math.min((i + 1) * 100 - 1, placeIdArray.length);
drawSpeedLimits(start, end);
}
}
// Gets speed limits for a 100-segment path and draws a polyline color-coded by
// speed limit. Must be called after processing snap-to-road response.
function drawSpeedLimits(start, end) {
var placeIdQuery = '';
for (var i = start; i < end; i++) {
placeIdQuery += '&placeId=' + placeIdArray[i];
}
$.get('https://roads.googleapis.com/v1/speedLimits',
'key=' + apiKey + placeIdQuery,
function(speedData) {
processSpeedLimitResponse(speedData, start);
}
);
}
// Draw a polyline segment (up to 100 road segments) color-coded by speed limit.
function processSpeedLimitResponse(speedData, start) {
var end = start + speedData.speedLimits.length;
for (var i = 0; i < speedData.speedLimits.length - 1; i++) {
var speedLimit = speedData.speedLimits[i].speedLimit;
var color = getColorForSpeed(speedLimit);
// Take two points for a single-segment polyline.
var coords = snappedCoordinates.slice(start + i, start + i + 2);
var snappedPolyline = new google.maps.Polyline({
path: coords,
strokeColor: color,
strokeWeight: 6
});
snappedPolyline.setMap(map);
polylines.push(snappedPolyline);
}
}
function getColorForSpeed(speed_kph) {
if (speed_kph <= 40) {
return 'purple';
}
if (speed_kph <= 50) {
return 'blue';
}
if (speed_kph <= 60) {
return 'green';
}
if (speed_kph <= 80) {
return 'yellow';
}
if (speed_kph <= 100) {
return 'orange';
}
return 'red';
}
$(window).load(initialize);
</script>
</head>
<body>
<div id="map"></div>
<div id="bar">
<p class="auto"><input type="text" id="autoc"/></p>
<p><a id="clear" href="#">Click here</a> to clear map.</p>
</div>
</body>
</html>
In my application I want to group items addresses by areas, my items already use Google maps API to choose address and has Lat/Long coordinates. I want to group them by areas automatically.
The example would be
If we have address https://www.google.com/maps/place/Erfurto+g.+1,+Vilnius+04220,+Lietuva/#54.6765968,25.2102183,15.5z/data=!4m2!3m1!1s0x46dd9398506f84bd:0x6cc62f1d26bc6613
It should automatically be assigned to area, marked here:
https://www.google.com/maps/place/Lazdynai,+Vilnius,+Lietuva/#54.6702541,25.1870655,14z/data=!4m2!3m1!1s0x46dd93a6cc53ba05:0x2600d18d4c454331
Areas should be also administered in my application.
As I understand I should store all MultiPolygon coordinates in my back-end, and then use some algorithm to find if the coordinates of address belong to that polygon? Am I right? Or I can fetch that somehow using Google Map API?
I made some very rough polygons (sorry if I insult anybody from Vilnius :) ), but I think you'll get the idea.
the main method you're looking for is google.maps.geometry.poly.containsLocation()
So I think this gives you the components you need; the plinciles. But if there is something specific you want to happen, please tell me.
<!DOCTYPE html>
<html>
<head>
<style>
html, body, #map-canvas {
height: 400px;
margin: 0px;
padding: 0px
}
#my_div {
border: 1px solid grey;
}
</style>
<script type="text/javascript" src="https://maps.googleapis.com/maps/api/js?libraries=geometry"></script>
<script>
// these are very rough estimations of the Elderships of Vilnius
// please use the correct boundaries
var polygonData = [
{name: 'Senamiestis', color: '#ff0000', points: [[54.68256489008578, 25.315074920654297],[54.67631238544733, 25.30803680419922],[54.670555259971174, 25.288639068603516],[54.6752205795425, 25.269927978515625],[54.68812186362444, 25.259113311767578],[54.69506701073871, 25.26529312133789],[54.68832031289468, 25.30099868774414]]},
{name: 'Rasos', color: '#00ff00', points: [[54.669066215366605, 25.305633544921875],[54.68554193480844, 25.329322814941406],[54.68335879002739, 25.362625122070312],[54.656754688760536, 25.345458984375],[54.610254981579146, 25.328292846679688],[54.60568162741719, 25.308380126953125],[54.65516583289068, 25.29705047607422]]},
{name: 'Antakalnis', color: '#0000ff', points: [[54.72699938009521, 25.30426025390625],[54.71589532099472, 25.34820556640625],[54.80780860259057, 25.500640869140625],[54.81967870427071, 25.335845947265625],[54.771385204918595, 25.300140380859375]]}
];
var polygons = [];
var map;
// random markers.
var markerData = [
[54.75478050308602,25.3638149499893],
[54.68324427673198,25.27517330646513],
[54.70583916710748,25.240154385566694],
[54.68453433466152,25.293562531471235],
[54.72900384013322,25.330534100532514],
[54.682078227560325,25.28394949436186],
[54.65034635955749,25.30793917179106]
];
var markers = [];
var mapOptions = {
zoom: 10,
center: new google.maps.LatLng(54.682611637187925,25.287838697433454), // Vilnius university
mapTypeId: google.maps.MapTypeId.ROADMAP
};
function initialize() {
map = new google.maps.Map(document.getElementById('map-canvas'), mapOptions);
// draw the markers
var markerLocations = toLatLng(markerData);
for(var i in markerLocations) {
markers.push( new google.maps.Marker({
position: markerLocations[i],
title: i, // I'll just set the index as title.
map: map
}) );
}
// draw the polygons
for(var j in polygonData) {
var points = toLatLng(polygonData[j].points);
polygons[j] = drawPolygon(points, polygonData[j].color, polygonData[j].name);
// let's see if markers are in this polygon
var content = '';
for(var i in markerLocations) {
if (google.maps.geometry.poly.containsLocation(markers[i].position, polygons[j])) {
// display
content += '<li>' + markers[i].title + '</li>';
// I guess what you really want to do, is put this data in an array, or so
}
}
document.getElementById('display-data').innerHTML += '<h3>' + polygonData[j].name + '</h3>' + '<ul>' + content + '</ul><hr/>';
}
}
// takes an array of coordinats, [ [], [], [] ] , and returns Google Maps LatLng objects
function toLatLng(point_arrays) {
var locations = [];
for(var i in point_arrays) {
locations.push( new google.maps.LatLng(point_arrays[i][0], point_arrays[i][1]));
}
return locations;
}
// draws a polygon
function drawPolygon(points, color, name) {
if(points.length < 3) {
return;
}
// #see https://developers.google.com/maps/documentation/javascript/examples/polygon-simple
polygon = new google.maps.Polygon({
paths: points,
strokeColor: color,
strokeOpacity: 0.8,
strokeWeight: 2,
fillColor: color,
fillOpacity: 0.35,
title: name,
map: map
});
return polygon;
}
google.maps.event.addDomListener(window, 'load', initialize);
</script>
<style>
ul, li {
list-style: none;
}
li {
display: inline;
border: 1px solid grey;
padding: 5px;
margin: 5px;
}
</style>
</head>
<body>
<div id="map-canvas"></div>
<div id="display-data"></div>
</body>
</html>
What you could do, is update the markers table in the database; add a field 'eldership'.
You send the data you get from my script to the server, with Ajax, you update the table (= you fill in the id of the eldership in the eldership field of the marker), so you only have to do this once (and you need to update the new markers ...).