Google Maps API - Overlay Custom Roads - javascript

I am having a few issues with the Google Maps API. I managed to make a cutom map using an image tile. which seems to be working OK. I am just wondering if there is a way to overlay roads on the map?
At the moment I am assuming there are 2 ways this is possible, either some built in function in the API, which I am having troubles finding. I have found paths etc, But I would like roads/streets to have labels, resize on zoom etc, as well as be able to toggle.
The other option was to do a second image tile and overlay that image, which I am not sure how todo at this moment.
If anyone has any info on this, or could point me in the right direction. It would be much appreciated.
/* <![CDATA[ */
/* Global variable that will contain the Google Maps object. */
var map = null
// Google Maps Demo object
var Demo = Demo || {};
// The path to your tile images.
Demo.ImagesBaseUrl = './mapImage/mapTiles/';
Demo.ImagesRoadsUrl = './mapImage/overlayRoads/';
// NuviaMap class
Demo.NuviaMap = function(container) {
// Create map
// This sets the default info for your map when it is initially loaded.
map = new google.maps.Map(container, {
zoom: 1,
center: new google.maps.LatLng(0, 0),
mapTypeControl: false,
streetViewControl: false,
zoomControl: true,
zoomControlOptions: {
style: google.maps.ZoomControlStyle.SMALL
}
});
// Set custom tiles
map.mapTypes.set('nuvia', new Demo.ImgMapType('nuvia', '#4E4E4E'));
map.setMapTypeId('nuvia');
// Loop through the marker info array and load them onto the map.
for (var key in markers) {
var markerData = markers[key];
var marker = new google.maps.Marker({
position: new google.maps.LatLng(markerData.lat, markerData.lng),
map: map,
title: markerData.title,
flat: markerData.flat,
visible: true,
infoBubble: new InfoBubble({
maxWidth: 300,
content: (markerData.image ? '<img src="' + markerData.image + '" width="80" align="left">' : '') + '<h3>' + markerData.title + '</h3>' + (markerData.info ? '<p>' + markerData.info + '</p>' : ''),
shadowStyle: 1,
padding: '10px'
}),
// You can use custom icons, default icons, etc. In my case since a city was a marker the circle icon works pretty well.
// I adjust the scale / size of the icon depending on what kind of city it is on my map.
icon: {
url: markerData.icon,
}
});
// We need to trap the click event for the marker so we can pop up an info bubble.
google.maps.event.addListener(marker, 'click', function() {
this.infoBubble.open(map, this);
});
activeMarkers.push(marker);
}
// This is dumb. We only want the markers to display at certain zoom levels so this handled that.
// Google should have a way to specify zoom levels for markers. Maybe they do but I could not find them.
google.maps.event.addListener(map, 'zoom_changed', function() {
var currentZoom = map.getZoom();
for (var i = 0; i < activeMarkers.length; i++) {
var thisTitle = activeMarkers[i].title;
if (markers[thisTitle]['zoom'][currentZoom])
activeMarkers[i].setVisible(true);
else
activeMarkers[i].setVisible(false);
}
});
// This handles the displaying of lat/lng info in the lat/lng info container defined above in the HTML.
google.maps.event.addListener(map, 'click', function(event) {
$('#latLng').html("Latitude: " + event.latLng.lat() + " " + ", longitude: " + event.latLng.lng());
});
// Listener to display the X/Y coordinates
google.maps.event.addListener(map, 'mousemove', function (event) {
displayCoord(event.latLng);
});
};
// ImgMapType class
Demo.ImgMapType = function(theme, backgroundColor) {
this.name = this._theme = theme;
this._backgroundColor = backgroundColor;
};
// Let Google know what size our tiles are and what our min/max zoom levels should be.
Demo.ImgMapType.prototype.tileSize = new google.maps.Size(256, 256);
Demo.ImgMapType.prototype.minZoom = 1;
Demo.ImgMapType.prototype.maxZoom = 5;
// Load the proper tile.
Demo.ImgMapType.prototype.getTile = function(coord, zoom, ownerDocument) {
var tilesCount = Math.pow(2, zoom);
if (coord.x >= tilesCount || coord.x < 0 || coord.y >= tilesCount || coord.y < 0) {
var div = ownerDocument.createElement('div');
div.style.width = this.tileSize.width + 'px';
div.style.height = this.tileSize.height + 'px';
div.style.backgroundColor = this._backgroundColor;
return div;
}
var img = ownerDocument.createElement('IMG');
img.width = this.tileSize.width;
img.height = this.tileSize.height;
// This tells what tile image to load based on zoom and coord info.
img.src = Demo.Utils.GetImageUrl('tile_' + zoom + '_' + coord.x + '-' + coord.y + '.png');
return img;
};
// Just basically returns the image using the path set way above and the name of the actual image file.
Demo.Utils = Demo.Utils || {};
Demo.Utils.GetImageUrl = function(image) {
return Demo.ImagesBaseUrl + image;
};
// Opacity.
Demo.Utils.SetOpacity = function(obj, opacity /* 0 to 100 */ ) {
obj.style.opacity = opacity / 100;
obj.style.filter = 'alpha(opacity=' + opacity + ')';
};
// Create ye ol' map.
google.maps.event.addDomListener(window, 'load', function() {
var nuviaMap = new Demo.NuviaMap(document.getElementById('nuvia-map'));
});
console.log('Ready Builder');
/* ]]> */
This is the JS I am working off so far, (Credits to http://www.cartographersguild.com/showthread.php?t=21088)

Related

Custom slider zoom bar on mapbox gl js?

I'm trying to implement a zoom bar in mapbox gl js but the only thing I found is this code from their documentation which adds an +, - and a reset. Is there anyway I can add a slider zoom level bar ? (A bar like this https://www.w3schools.com/howto/howto_js_rangeslider.asp )
var nav = new mapboxgl.NavigationControl();
map.addControl(nav, 'bottom-left');
Yes, you can, it requires to create a custom control and add manually the event to update the map zoom... but it's only a few lines of code. I didn't work too much in the css styling.
here's a fiddle I have created with an example How to create a custom zoom control
And here's the relevant scripting code
<script>
mapboxgl.accessToken = 'PUT HERE YOUR MAPBOX TOKEN';
var map = new mapboxgl.Map({
container: 'map',
style: 'mapbox://styles/mapbox/streets-v11', // stylesheet location
zoom: 3, // starting zoom
center: [-95, 40], // starting position [lng, lat]
});
map.on('load', function () {
let zoomControl = new CustomZoomControl();
map.addControl(zoomControl, 'top-right');
map.on('zoom', function () {
zoomControl.update();
});
});
class CustomZoomControl {
onAdd(map) {
this.map = map;
this.container = document.createElement('div');
this.container.className = " mapboxgl-ctrl mapboxgl-ctrl-group";
this.input = document.createElement('input');
this.input.type = "range"
this.input.min = 1;
this.input.max = 220;
this.createAttribute(this.input , "value", map.getZoom()*10)
this.input.className = "slider";
this.input.id = "myRange";
this.container.appendChild(this.input);
// Update the current slider value (each time you drag the slider handle)
this.input.oninput = function () {
map.setZoom(this.value/10);
}
return this.container;
}
onRemove() {
this.container.parentNode.removeChild(this.container);
this.map = undefined;
}
createAttribute(obj, attrName, attrValue) {
var att = document.createAttribute(attrName);
att.value = attrValue;
obj.setAttributeNode(att);
}
update() {
let zoom = map.getZoom() * 10;
if (this.input.value != zoom) this.input.value = zoom;
}
}
</script>

Here Maps polyline with altitude

I need to display different polylines from A to B. So, these lines should be distinguishable from each other. I haved tried to set polylines using pushpoint function with altitude parameter. However it is still on the ground level. And the last polyline I inserted overwrites the previous one.
Altitude value works on markers but I want to apply it on polyline.
I changed the sample code here markers with altitude as below. You can see the orange line is just on top of the gray line when you change the code with the below one. I would like both lines to be displayed like the markers you see above them.
/**
* Calculate the bicycle route.
* #param {H.service.Platform} platform A stub class to access HERE services
*/
function calculateRouteFromAtoB (platform) {
var router = platform.getRoutingService(),
routeRequestParams = {
mode: 'fastest;bicycle',
representation: 'display',
routeattributes : 'shape',
waypoint0: '-16.1647142,-67.7229166',
waypoint1: '-16.3705847,-68.0452683',
// explicitly request altitude values
returnElevation: true
};
router.calculateRoute(
routeRequestParams,
onSuccess,
onError
);
}
/**
* Process the routing response and visualise the descent with the help of the
* H.map.Marker
*/
function onSuccess(result) {
var lineString = new H.geo.LineString(),
lineString2 = new H.geo.LineString(),
routeShape = result.response.route[0].shape,
group = new H.map.Group(),
dict = {},
polyline,
polyline2;
routeShape.forEach(function(point) {
var parts = point.split(',');
var pp= new H.geo.Point(parts[0],parts[1],4000,"SL");
console.log(parts[2]);
lineString.pushLatLngAlt(parts[0], parts[1]);
lineString2.pushPoint(pp);
// normalize the altitude values for the color range
var p = (parts[2] - 1000) / (4700 - 1000);
var r = Math.round(255 * p);
var b = Math.round(255 - 255 * p);
// create or re-use icon
var icon;
if (dict[r + '_' + b]) {
icon = dict[r + '_' + b];
} else {
var canvas = document.createElement('canvas');
canvas.width = 4;
canvas.height = 4;
var ctx = canvas.getContext('2d');
ctx.fillStyle = 'rgb(' + r + ', 0, ' + b + ')';
ctx.fillRect(0, 0, 4, 4);
icon = new H.map.Icon(canvas);
// cache the icon for the future reuse
dict[r + '_' + b] = icon;
}
// the marker is placed at the provided altitude
var marker = new H.map.Marker({
lat: parts[0], lng: parts[1], alt: parts[2]
}, {icon: icon});
var marker2 = new H.map.Marker({
lat: parts[0], lng: parts[1], alt: parts[2]-800
}, {icon: icon});
group.addObject(marker);
group.addObject(marker2);
});
polyline = new H.map.Polyline(lineString, {
style: {
lineWidth: 6,
strokeColor: '#555555'
}
});
polyline2 = new H.map.Polyline(lineString2, {
style: {
lineWidth: 3,
strokeColor: '#FF5733'
}
});
// Add the polyline to the map
map.addObject(polyline);
map.addObject(polyline2);
// Add markers to the map
map.addObject(group);
// Zoom to its bounding rectangle
map.getViewModel().setLookAtData({
bounds: polyline.getBoundingBox(),
tilt: 60
});
}
/**
* This function will be called if a communication error occurs during the JSON-P request
* #param {Object} error The error message received.
*/
function onError(error) {
alert('Can\'t reach the remote server');
}
/**
* Boilerplate map initialization code starts below:
*/
// set up containers for the map + panel
var mapContainer = document.getElementById('map'),
routeInstructionsContainer = document.getElementById('panel');
//Step 1: initialize communication with the platform
// In your own code, replace variable window.apikey with your own apikey
var platform = new H.service.Platform({
apikey: window.apikey
});
var defaultLayers = platform.createDefaultLayers();
//Step 2: initialize a map - this map is centered over Berlin
var map = new H.Map(mapContainer,
defaultLayers.vector.normal.map,{
center: {lat:52.5160, lng:13.3779},
zoom: 13,
pixelRatio: window.devicePixelRatio || 1
});
// add a resize listener to make sure that the map occupies the whole container
window.addEventListener('resize', () => map.getViewPort().resize());
//Step 3: make the map interactive
// MapEvents enables the event system
// Behavior implements default interactions for pan/zoom (also on mobile touch environments)
var behavior = new H.mapevents.Behavior(new H.mapevents.MapEvents(map));
// Create the default UI components
var ui = H.ui.UI.createDefault(map, defaultLayers);
// Now use the map as required...
calculateRouteFromAtoB (platform);
Unfortunately, for now only markers support altitudes.
Polylines should follow in near future.

How to curve a Polyline in react-google-maps?

I'm new to React and have been playing around with the react-google-maps package. I'm trying to curve a Polyline that joins two places. After going through the documentation, I'm trying to incorporate the curve polyline function under the 'editable' prop.
Here's the function to curve the polyline:
var map;
var curvature = 0.4; // Arc of the Polyline
function init() {
var Map = google.maps.Map,
LatLng = google.maps.LatLng,
LatLngBounds = google.maps.LatLngBounds,
Marker = google.maps.Marker,
Point = google.maps.Point;
// Initial location of the points
var pos1 = new LatLng(this.state.srcMarker);
var pos2 = new LatLng(this.state.desMarker);
var bounds = new LatLngBounds();
bounds.extend(pos1);
bounds.extend(pos2);
map = new Map(document.getElementById('map-canvas'), {
center: bounds.getCenter(),
zoom: 12
});
map.fitBounds(bounds);
var markerP1 = new Marker({
position: pos1,
map: map
});
var markerP2 = new Marker({
position: pos2,
map: map
});
var curveMarker;
function updateCurveMarker() {
var pos1 = markerP1.getPosition(),
pos2 = markerP2.getPosition(),
projection = map.getProjection(),
p1 = projection.fromLatLngToPoint(pos1),
p2 = projection.fromLatLngToPoint(pos2);
// Calculating the arc.
var e = new Point(p2.x - p1.x, p2.y - p1.y), // endpoint
m = new Point(e.x / 2, e.y / 2), // midpoint
o = new Point(e.y, -e.x), // orthogonal
c = new Point( m.x + curvature * o.x, m.y + curvature * o.y); //curve control point
var pathDef = 'M 0,0 ' + 'q ' + c.x + ',' + c.y + ' ' + e.x + ',' + e.y;
var zoom = map.getZoom(),
scale = 1 / (Math.pow(2, -zoom));
var symbol = {
path: pathDef,
scale: scale,
strokeWeight: 1,
fillColor: 'none'
};
if (!curveMarker) {
curveMarker = new Marker({
position: pos1,
clickable: false,
icon: symbol,
zIndex: 0, // behind the other markers
map: map
});
} else {
curveMarker.setOptions({
position: pos1,
icon: symbol,
});
}
}
google.maps.event.addListener(map, 'projection_changed', updateCurveMarker);
google.maps.event.addListener(map, 'zoom_changed', updateCurveMarker);
google.maps.event.addListener(markerP1, 'position_changed', updateCurveMarker);
google.maps.event.addListener(markerP2, 'position_changed', updateCurveMarker);
}
google.maps.event.addDomListener(window, 'load', init);
I'm not able to understand how to use this function in the Polyline component. I'm able to mark a line between any two places, but not able to use this function in order to curve the given polyline. This is the Polyline component that I'm using.
<Polyline
path={pathCoordinates}
geodesic={true}
options={{
strokeColor: '#ff2527',
strokeOpacity: 1.0,
strokeWeight: 5,
}}
/>
I have two markers in my state (srcMarker, desMarker) that store the coordinates of the given cities once the user inputs the city name. Any help would be appreciated in incorporating this function with the Polyline component. I haven't come across any built in feature that allows curving of the polyline. Thanks in advance!
I took the code you provided and adapted it to work with React and react-google-maps. Check out this CodeSandbox to see a simple application that contains two markers and a curved line between them.
The curved line that connects the two markers is actually a marker as well. The only difference between it and the two red markers is that its icon prop is set to the curved line (which is computed beforehand).
Here is the code for the CurveMarker component:
const CurveMarker = ({ pos1, pos2, mapProjection, zoom }) => {
if (!mapProjection) return <div/>;
var curvature = 0.4
const p1 = mapProjection.fromLatLngToPoint(pos1),
p2 = mapProjection.fromLatLngToPoint(pos2);
// Calculating the arc.
const e = new google.maps.Point(p2.x - p1.x, p2.y - p1.y), // endpoint
m = new google.maps.Point(e.x / 2, e.y / 2), // midpoint
o = new google.maps.Point(e.y, -e.x), // orthogonal
c = new google.maps.Point(m.x + curvature * o.x, m.y + curvature * o.y); //curve control point
const pathDef = 'M 0,0 ' + 'q ' + c.x + ',' + c.y + ' ' + e.x + ',' + e.y;
const scale = 1 / (Math.pow(2, -zoom));
const symbol = {
path: pathDef,
scale: scale,
strokeWeight: 2,
fillColor: 'none'
};
return <Marker
position={pos1}
clickable={false}
icon={symbol}
zIndex={0}
/>;
};
Let me know if you have any questions.

Undefinded Global Variable JS

i'm making an android app with cordova. And a I have a noob problem.
I have a map in the index.html, when the index initializes the app obtains the position of the mobile and later does a map with a marker where the mobile is.
The code is this:
var longitud;
var latitud;
function getPosition() {
var options = {
enableHighAccuracy: true,
maximumAge: 1
}
var watchID = navigator.geolocation.getCurrentPosition(onSuccess, onError, options);
function onSuccess(position) {
this.latitud = position.coords.latitude;
this.longitud = position.coords.longitude;
navigator.geolocation.clearWatch(watchID);
mapa();
};
function onError(error) {
alert('code: ' + error.code + '\n' + 'message: ' + error.message + '\n');
}
}
function mapa() {
alert(this.latitud + ' ' + this.longitud);
document.getElementById("mapa").innerHTML = "";
map = new OpenLayers.Map("mapa");
var mapnik = new OpenLayers.Layer.OSM();
var fromProjection = new OpenLayers.Projection("EPSG:4326"); // Transform from WGS 1984
var toProjection = new OpenLayers.Projection("EPSG:900913"); // to Spherical Mercator Projection
var position = new OpenLayers.LonLat(window.longitud, window.latitud).transform(fromProjection, toProjection);
var zoom = 17;
map.addLayer(mapnik);
map.setCenter(position, zoom);
var lonLat = new OpenLayers.LonLat(this.longitud, this.latitud)
.transform(
new OpenLayers.Projection("EPSG:4326"), // transform from WGS 1984
map.getProjectionObject() // to Spherical Mercator Projection
);
var markers = new OpenLayers.Layer.Markers("Markers");
map.addLayer(markers);
//markers.icon(img/ico-taxi);
markers.addMarker(new OpenLayers.Marker(lonLat));
}
This works well in the index.html. But in other page i want to do the same thing, but instead of showing always, only show it if the users click the accordion because the users must know where are they but I dont want to have a big map in this page all time. When user open the accordion the app creates a map using the same functions because the div id is the same. This works well too, but additionally i want to put the coordinates inside two inputs: inputLat and Input Lon. When user click the accordion in the inputs appears undefined but in theory were defined in index.html and when he clicks in the accordion. But the most strange thing is that, when I close the accordion and I open it again, the coordinates appears. How I can have the coordinates in the input the first time i clik accordion? I give you the code of the accordion functions.
function comprobar() {
var x = document.getElementById("mapa");
if (x.className.indexOf("w3-show") == -1) {
x.className += " w3-show";
getPosition();
geoLocalizacion();
} else {
x.className = x.className.replace(" w3-show", "");
}
}
function geoLocalizacion () {
document.getElementById("inputLat").value = this.latitud;
document.getElementById("inputLon").value = this.longitud;
}
I tried to read other similar posts and doesn't work. If I add var Longitud = this; int the declaration it shows [object Window]
javascript returns undefind for a globally declared variable
Why a variable defined global is undefined?
Thanks to all I'm so sure that is a noob thing.

Mouse over is not triggered while dragging marker Google Maps API

In my web page, I am trying to use google map in which i can drag and drop markers around. The problem is that while i drag a special marker, the mouse over and mouse out events of the other markers are not triggered .
Well, i understand that a Google Map uses the following layers:
floatPane, overlayMouseTarget, floatShadow, overlayImage, overlayShadow, overlayLayer, mapPane,
and the markers belong to overlayImage layer.
Also, the reason why the mouse over and out events are not triggered is because
the draggable marker "absorbs" the events that never reach to the marker behind.
Example: I have markers m1, m2 and m3. Marker m3 is draggable. I want to drag m3 around and when it passes on top of m1 and m2 , mouse over events of m1 and m2 to be triggered.
Question 1: In which layer is the draggable object belongs ? (I assume overlayMouseTarget).
Question 2: Can i change the layer that a marker belongs ? That way i will be able to modify it's z-index.
Thank you for your time.
This is a great case of a question where you are asking how to do something that you don't need to do. You don't want to play with the layers at all, you want to monitor the drag event and check the proximity of the marker that is being dragged to the other markers.
You won't actually want to trigger the events, but you want to call the functions that are attached to those events.
Warning - Lots of Code Ahead
var map = new google.maps.Map(document.getElementById('map'),{
zoom: 15,
center: {lat:44.6478627,lng:-63.6116746}
});
var m1 = new google.maps.Marker({
map: map,
position: {lat:44.6566246,lng:-63.6202576}
});
var m2 = new google.maps.Marker({
map: map,
position: {lat:44.6566475,lng:-63.6197212}
});
var m3 = new google.maps.Marker({
draggable:true,
map: map,
position: {lat:44.6544724,lng:-63.6296561}
});
//some useful flags
var isDragging = false;
var hoveredMarker = null;
//a tolerance amount - how close the dragging marker has to be to another marker in order for you to consider it to be a mouseover
//you may need to play with this number to achieve the effect you want
var tolerance = 30;
var methods = {
fromLatLngToPoint: function(latLng) {
var topRight = map.getProjection().fromLatLngToPoint(map.getBounds().getNorthEast());
var bottomLeft = map.getProjection().fromLatLngToPoint(map.getBounds().getSouthWest());
var scale = Math.pow(2, map.getZoom());
var worldPoint = map.getProjection().fromLatLngToPoint(latLng);
return new google.maps.Point((worldPoint.x - bottomLeft.x) * scale, (worldPoint.y - topRight.y) * scale);
},
onStartDragging: function() {
isDragging = true;
},
onStopDragging: function() {
isDragging = false;
},
onMarkerMouseOver: function(marker) {
//set the hoveredMarker flag to the marker that is being hovered
hoveredMarker = marker;
if(isDragging) {
//do something here when you mouse over `marker`, only if you are also dragging
console.log('mouse over while dragging');
}
//do something here when you mouse over `marker`, regardless of if you are dragging or not
//console.log('mouse over');
},
onMarkerMouseOut: function(marker) {
//set the hoveredMarker flag to null
hoveredMarker = null;
if(isDragging) {
//do something here when you mouse out `marker`, only if you are also dragging
console.log('mouse out while dragging');
}
//do something here when you mouse out `marker`, regardless of if you are dragging or not
//console.log('mouse out');
},
arePointsClose: function(latLng1,latLng2) {
var
p1 = this.fromLatLngToPoint(latLng1),
p2 = this.fromLatLngToPoint(latLng2);
//if the points are within `tolerance` return true, otherwise, return false
return Math.abs( p2.x - p1.x ) <= tolerance && Math.abs( p2.y - p1.y ) <= tolerance;
},
onDrag: function(e) {
//it's very unlikely that you're coordinates of m3 will ever be 100% identical to m1 or m2 when dragging,
//so use the maps projection to convert to latLngs to a point and allow a tolerance ( see methods.arePointsClose() )
if( methods.arePointsClose(e.latLng,m1.getPosition()) ) {
methods.onMarkerMouseOver(m1);
} else if( hoveredMarker === m1 ) {
methods.onMarkerMouseOut(m1);
}
if( methods.arePointsClose(e.latLng,m2.getPosition()) ) {
methods.onMarkerMouseOver(m2);
} else if( hoveredMarker === m2 ) {
methods.onMarkerMouseOut(m2);
}
}
};
google.maps.event.addListener(m1,'mouseover',function() { methods.onMarkerMouseOver(m1); });
google.maps.event.addListener(m2,'mouseover',function() { methods.onMarkerMouseOver(m2); });
google.maps.event.addListener(m1,'mouseout',function() { methods.onMarkerMouseOut(m1); });
google.maps.event.addListener(m2,'mouseout',function() { methods.onMarkerMouseOut(m2); });
google.maps.event.addListener(m3,'dragstart',function() { methods.onStartDragging(); });
google.maps.event.addListener(m3,'dragend',function() { methods.onStopDragging(); });
google.maps.event.addListener(m3,'drag',function(event) { methods.onDrag(event); });
html,body { height: 100%; }
#map { height: 100%; }
<script type="text/javascript" src="https://maps.googleapis.com/maps/api/js?libraries=drawing,geometry"></script>
<div id="map"></div>

Categories