Add custom background image to draggable polygons in google map? - google-maps

I want to add custom background image to my draggable polygons in google Map. I've already used the Polygon class to make a draggable polygon that can also rotate. I want to add background image to it. I've read other posts and they mentioned "custom overlay" but that is a fixed image on the map which doesn't support dragging/rotation. How should I go about doing this?
Update:
I created a custom layer with my image and added it to the map with the same coordinates as the polygon. Whenever the bounds of my polygon change, I will also update my custom layer so they always overlap. However, as shown in the gif, https://imgur.com/3oaktIY, the polygon and the image are not in sync and there's a delay.
Is there any other way to do it?

Did not find solutions online so I figured it out myself with this working demo: draggable polygon with image. I made it with a combination of custom overlay and normal polygon library. You can click on the polygon to rotate and drag it around.
plz see jsfiddle
code snippet:
// This example adds hide() and show() methods to a custom overlay's prototype.
// These methods toggle the visibility of the container <div>.
// Additionally, we add a toggleDOM() method, which attaches or detaches the
// overlay to or from the map.
var overlay;
USGSOverlay.prototype = new google.maps.OverlayView();
function initMap() {
var map = new google.maps.Map(document.getElementById('map'), {
zoom: 20,
center: {
lat: 33.678,
lng: -116.243
},
mapTypeId: google.maps.MapTypeId.TERRAIN
});
var rectangle = new google.maps.Rectangle({
strokeColor: 'red',
strokeOpacity: 0,
strokeWeight: 2,
fillColor: '#FF0000',
fillOpacity: 0,
map: map,
bounds: calcBounds(map.getCenter(), new google.maps.Size(2.7, 20))
});
var rectPoly = createPolygonFromRectangle(rectangle); //create a polygom from a rectangle
rectPoly.addListener('click', function(e) {
rotatePolygon(rectPoly, 10);
rectPoly.rotation += 10;
console.log(rectPoly.rotation)
overlay.div_.style.transform = 'rotate(' + rectPoly.rotation + 'deg)';
});
rectPoly.addListener('drag', function() {
console.log('Drag end!');
let bounds = new google.maps.LatLngBounds();
var i;
// The Bermuda Triangle
let polygonCoords = rectPoly.getPath().getArray();
for (i = 0; i < polygonCoords.length; i++) {
bounds.extend(polygonCoords[i]);
}
// The Center of the Bermuda Triangle - (25.3939245, -72.473816)
center = bounds.getCenter();
overlay.bounds_ = calcBounds(center, new google.maps.Size(2.7, 20))
overlay.draw();
});
function calcBounds(center, size) {
var n = google.maps.geometry.spherical.computeOffset(center, size.height / 2, 0).lat(),
s = google.maps.geometry.spherical.computeOffset(center, size.height / 2, 180).lat(),
e = google.maps.geometry.spherical.computeOffset(center, size.width / 2, 90).lng(),
w = google.maps.geometry.spherical.computeOffset(center, size.width / 2, 270).lng();
return new google.maps.LatLngBounds(new google.maps.LatLng(s, w),
new google.maps.LatLng(n, e))
}
var srcImage = 'https://developers.google.com/maps/documentation/' +
'javascript/examples/full/images/talkeetna.png';
overlay = new USGSOverlay(rectangle.bounds, srcImage, map, rectPoly);
// The custom USGSOverlay object contains the USGS image,
// the bounds of the image, and a reference to the map.
}
/** #constructor */
function USGSOverlay(bounds, image, map, rectPoly) {
// Now initialize all properties.
this.bounds_ = bounds;
this.image_ = image;
this.map_ = map;
this.rectPoly_ = rectPoly
// 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.
*/
USGSOverlay.prototype.onAdd = function() {
var div = document.createElement('div');
div.style.border = 'none';
div.style.borderWidth = '0px';
div.style.position = 'absolute';
rectPoly = this.rectPoly_;
// 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%';
div.appendChild(img);
this.div_ = div;
// Add the element to the "overlayImage" pane.
var panes = this.getPanes();
panes.overlayImage.appendChild(this.div_);
};
USGSOverlay.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';
};
USGSOverlay.prototype.onRemove = function() {
this.div_.parentNode.removeChild(this.div_);
};
// Set the visibility to 'hidden' or 'visible'.
function createPolygonFromRectangle(rectangle) {
var map = rectangle.getMap();
var coords = [{
lat: rectangle.getBounds().getNorthEast().lat(),
lng: rectangle.getBounds().getNorthEast().lng()
},
{
lat: rectangle.getBounds().getNorthEast().lat(),
lng: rectangle.getBounds().getSouthWest().lng()
},
{
lat: rectangle.getBounds().getSouthWest().lat(),
lng: rectangle.getBounds().getSouthWest().lng()
},
{
lat: rectangle.getBounds().getSouthWest().lat(),
lng: rectangle.getBounds().getNorthEast().lng()
}
];
// Construct the polygon.
var rectPoly = new google.maps.Polygon({
path: coords,
draggable: true,
rotation: 0
});
var properties = ["strokeColor", "strokeOpacity", "strokeWeight", "fillOpacity", "fillColor"];
//inherit rectangle properties
var options = {};
properties.forEach(function(property) {
if (rectangle.hasOwnProperty(property)) {
options[property] = rectangle[property];
}
});
rectPoly.setOptions(options);
rectangle.setMap(null);
rectPoly.setMap(map);
return rectPoly;
}
function rotatePolygon(polygon, angle) {
var map = polygon.getMap();
var prj = map.getProjection();
var bounds = new google.maps.LatLngBounds();
var i;
// The Bermuda Triangle
var polygonCoords = polygon.getPath().getArray();
for (i = 0; i < polygonCoords.length; i++) {
bounds.extend(polygonCoords[i]);
}
// The Center of the Bermuda Triangle - (25.3939245, -72.473816)
console.log(bounds.getCenter());
var origin = prj.fromLatLngToPoint(bounds.getCenter()); //rotate around first point
//var origin2 = prj.fromLatLngToPoint(polygon.getPath().getAt(1)); //rotate around first point
var coords = polygon.getPath().getArray().map(function(latLng) {
var point = prj.fromLatLngToPoint(latLng);
var rotatedLatLng = prj.fromPointToLatLng(rotatePoint(point, origin, angle));
return {
lat: rotatedLatLng.lat(),
lng: rotatedLatLng.lng()
};
});
polygon.setPath(coords);
}
function rotatePoint(point, origin, angle) {
var angleRad = angle * Math.PI / 180.0;
return {
x: Math.cos(angleRad) * (point.x - origin.x) - Math.sin(angleRad) * (point.y - origin.y) + origin.x,
y: Math.sin(angleRad) * (point.x - origin.x) + Math.cos(angleRad) * (point.y - origin.y) + origin.y
};
}
google.maps.event.addDomListener(window, 'load', initMap);
/* Always set the map height explicitly to define the size of the div
* element that contains the map. */
#map {
height: 100%;
}
/* 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;
}
<!-- Add an input button to initiate the toggle method on the overlay. -->
<div id="map"></div>
<!-- Replace the value of the key parameter with your own API key. -->
<script src="https://maps.googleapis.com/maps/api/js?key=AIzaSyCkUOdZ5y7hMm0yrcCQoCvLwzdM6M8s5qk&libraries=geometry"></script>

Related

Google maps how to calculate south west and north east bound for overlay

While displaying an overlay over google maps using the API. I am googling for last three days how to calculate the South West and North east bounds for an overlay of size 100X100. I found millions of examples but the bounds were written hard coded.
My input is Center Latitude and Longitude and a 100x100 image.
The code I found is:
function initMap() {
var map = new google.maps.Map(document.getElementById('map'), {
zoom: 11,
center: {lat: 62.323907, lng: -150.109291},
mapTypeId: google.maps.MapTypeId.SATELLITE
});
var bounds = new google.maps.LatLngBounds(
new google.maps.LatLng(SE),
new google.maps.LatLng(NE));
// The photograph is courtesy of the U.S. Geological Survey.
var srcImage = 'https://developers.google.com/maps/documentation/javascript/';
srcImage += 'examples/full/images/talkeetna.png';
overlay = new USGSOverlay(bounds, srcImage, map);
}
I am woundering how these bounds were calculated?
SE and NE
To calculate the bounds (specifically a google.maps.LatLngBounds object) that is 100px x 100px centered at the center of the bounds of that overlay:
var bounds = new google.maps.LatLngBounds(
new google.maps.LatLng(62.281819, -150.287132),
new google.maps.LatLng(62.400471, -150.005608));
find the pixel position of the center:
var center = bounds.getCenter();
var centerPt = overlay.getProjection().fromLatLngToDivPixel(center);
find the pixel point 50px up and 50px right of it
var topRightPt = new google.maps.Point(centerPt.x+50, centerPt.y+50);
find the pixel point 50px down and 50px left of it
var botLeftPt = new google.maps.Point(centerPt.x-50, centerPt.y-50);
translate those back to latitude and longitude coordinates
var sw = overlay.getProjection().fromDivPixelToLatLng(botLeftPt);
var ne = overlay.getProjection().fromDivPixelToLatLng(topRightPt);
proof of concept fiddle
overlay = new USGSOverlay(bounds, srcImage, map);
google.maps.event.addListener(overlay, 'projection_changed', function() {
var centerPt = overlay.getProjection().fromLatLngToDivPixel(center);
var topRightPt = new google.maps.Point(centerPt.x+50, centerPt.y+50);
var botLeftPt = new google.maps.Point(centerPt.x-50, centerPt.y-50);
var sw = overlay.getProjection().fromDivPixelToLatLng(botLeftPt);
var ne = overlay.getProjection().fromDivPixelToLatLng(topRightPt);
var overlayBnds = new google.maps.LatLngBounds(sw,ne);
var rectangle = new google.maps.Rectangle({
map: map,
bounds: overlayBnds
})
});
code snippet:
google.maps.event.addDomListener(window, "load", initMap);
// This example creates a custom overlay called USGSOverlay, 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.
var overlay;
USGSOverlay.prototype = new google.maps.OverlayView();
// Initialize the map and the custom overlay.
function initMap() {
var map = new google.maps.Map(document.getElementById('map'), {
zoom: 11,
center: {
lat: 62.323907,
lng: -150.109291
},
mapTypeId: 'satellite'
});
var bounds = new google.maps.LatLngBounds(
new google.maps.LatLng(62.281819, -150.287132),
new google.maps.LatLng(62.400471, -150.005608));
var center = bounds.getCenter();
// The photograph is courtesy of the U.S. Geological Survey.
var srcImage = 'https://developers.google.com/maps/documentation/' +
'javascript/examples/full/images/talkeetna.png';
// The custom USGSOverlay object contains the USGS image,
// the bounds of the image, and a reference to the map.
overlay = new USGSOverlay(bounds, srcImage, map);
google.maps.event.addListener(overlay, 'projection_changed', function() {
var centerPt = overlay.getProjection().fromLatLngToDivPixel(center);
var topRightPt = new google.maps.Point(centerPt.x + 50, centerPt.y + 50);
var botLeftPt = new google.maps.Point(centerPt.x - 50, centerPt.y - 50);
var sw = overlay.getProjection().fromDivPixelToLatLng(botLeftPt);
var ne = overlay.getProjection().fromDivPixelToLatLng(topRightPt);
var overlayBnds = new google.maps.LatLngBounds(sw, ne);
var rectangle = new google.maps.Rectangle({
map: map,
bounds: overlayBnds
})
});
}
/** #constructor */
function USGSOverlay(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.
*/
USGSOverlay.prototype.onAdd = function() {
var div = document.createElement('div');
div.style.borderStyle = 'none';
div.style.borderWidth = '0px';
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);
this.div_ = div;
// Add the element to the "overlayLayer" pane.
var panes = this.getPanes();
panes.overlayLayer.appendChild(div);
};
USGSOverlay.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';
};
// The onRemove() method will be called automatically from the API if
// we ever set the overlay's map property to 'null'.
USGSOverlay.prototype.onRemove = function() {
this.div_.parentNode.removeChild(this.div_);
this.div_ = null;
};
html,
body,
#map {
height: 100%;
width: 100%;
margin: 0px;
padding: 0px
}
<script src="https://maps.googleapis.com/maps/api/js?key=AIzaSyCkUOdZ5y7hMm0yrcCQoCvLwzdM6M8s5qk"></script>
<div id="map"></div>

Marker shadows in Google Maps v3

I'm looking for a way to make marker shadows work with the "visual refresh" that's coming to Google Maps, but can't seem to.
I suppose the following is the recommended way to do it:
https://developers.google.com/maps/tutorials/customizing/custom-markers
Can anyone see the marker shadows in the examples on that tutorial? I can't.
What are you guys doing to make this work?
Thanks in advance!
One option for adding shadows to post visual refresh Google Maps Javascript API v3 maps:
create a Custom Overlay to hold the shadow image attached to the "overlayShadow" pane
add one for every marker
proof of concept
Add markers with shadows like this:
var marker = new google.maps.Marker({
position: myLatLng,
map: map,
icon: getMarkerImage(beach[4]),
shape: iconShape,
title: beach[0],
zIndex: Math.round(myLatLng.lat()*-100000)<<5
});
var shadow = new MarkerShadow(myLatLng, iconShadow, map);
marker.bindTo('map',shadow,'map');
MarkerShadow code (modified from Google's Custom Overlay example):
MarkerShadow.prototype = new google.maps.OverlayView();
/** #constructor */
function MarkerShadow(position, options, map) {
// Initialize all properties.
this.posn_ = position;
this.map_ = map;
if (typeof(options) == "string") {
this.image = options;
} else {
this.options_ = options;
if (!!options.size) this.size_ = options.size;
if (!!options.url) this.image_ = options.url;
}
// 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.
*/
MarkerShadow.prototype.onAdd = function() {
// if no url, return, nothing to do.
if (!this.image_) return;
var div = document.createElement('div');
div.style.borderStyle = 'none';
div.style.borderWidth = '0px';
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 = this.options_.size.x + 'px';
img.style.height = this.options_.size.y +'px';
img.style.position = 'absolute';
div.appendChild(img);
this.div_ = div;
// Add the element to the "overlayLayer" pane.
var panes = this.getPanes();
panes.overlayShadow.appendChild(div);
};
MarkerShadow.prototype.draw = function() {
// if no url, return, nothing to do.
if (!this.image_) return;
// We use the coordinates of the overlay to peg it to the correct position
// To do this, we need to retrieve the projection from the overlay.
var overlayProjection = this.getProjection();
var posn = overlayProjection.fromLatLngToDivPixel(this.posn_);
// Resize the image's div to fit the indicated dimensions.
if (!this.div_) return;
var div = this.div_;
if (!!this.options_.anchor) {
div.style.left = Math.floor(posn.x-this.options_.anchor.x) + 'px';
div.style.top = Math.floor(posn.y-this.options_.anchor.y) + 'px';
}
if (!!this.options_.size) {
div.style.width = this.size_.x + 'px';
div.style.height = this.size_.y + 'px';
}
};
// The onRemove() method will be called automatically from the API if
// we ever set the overlay's map property to 'null'.
MarkerShadow.prototype.onRemove = function() {
if (!this.div_) return;
this.div_.parentNode.removeChild(this.div_);
this.div_ = null;
};
Why not create an extra marker with lower z-index?
createMarkerShadow = function(map, data) {
var latLng = new google.maps.LatLng(data.latitude, data.longitude);
var markerShadow = new google.maps.Marker({
clickable: false,
position: latLng,
map: map,
icon:{
url: '/frontend/img/icons/google-map-marker-shadow.svg',
//The size image file.
size: new google.maps.Size(225, 120),
//The point on the image to measure the anchor from. 0, 0 is the top left.
origin: new google.maps.Point(0, 0),
//The x y coordinates of the anchor point on the marker. e.g. If your map marker was a drawing pin then the anchor would be the tip of the pin.
anchor: new google.maps.Point(115, 82)
},
zIndex: (Math.round(latLng.lat()*-100000)<<5)-1
});
return markerShadow;
};
setMarkerShadows = function (map, locations, bound) {
for (var i = 0; i < locations.length; i++) {
var data = locations[i];
var markerShadow = createMarkerShadow(map, data);
bound.extend(markerShadow.getPosition());
}
};
bound = new google.maps.LatLngBounds();

Display Text inside a circle marker

I want to display a circle like in this sample:
https://developers.google.com/maps/documentation/javascript/examples/circle-simple?hl=en
I do not want to use clustered markers as I really just need to display some circles with a
number inside.
UPDATE
That is my code:
$(document).ready(function() {
//Google maps API initialisation
var overlay;
USGSOverlay.prototype = new google.maps.OverlayView();
/* constructor */
function USGSOverlay(bounds, map, alarmnumber) {
// Now initialize all properties.
this.bounds_ = bounds;
this.map_ = map;
this.alarmnumber_ = alarmnumber;
// We 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);
}
USGSOverlay.prototype.onAdd = function() {
// Note: an overlay's receipt of onAdd() indicates that
// the map's panes are now available for attaching
// the overlay to the map via the DOM.
// Create the DIV and set some basic attributes.
var div = document.createElement('div');
div.style.borderStyle = 'none';
div.style.borderWidth = '0px';
div.style.position = 'absolute';
div.style.border = '1px solid #000';
div.style.fontWeight = 'bold';
div.style.backgroundColor = '#fff';
$(div).attr('id', 'alarmDiv' + this.alarmnumber_);
$(div).addClass("circle");
$(div).addClass("center");
// Set the overlay's div_ property to this DIV
this.div_ = div;
this.div_.innerHTML = this.alarmnumber_;
// We add an overlay to a map via one of the map's panes.
// We'll add this overlay to the overlayLayer pane.
var panes = this.getPanes();
panes.overlayLayer.appendChild(div);
};
USGSOverlay.prototype.draw = function() {
// Size and position the overlay. We use a southwest and northeast
// position of the overlay to peg it to the correct position and size.
// We need to retrieve the projection from this overlay to do this.
var overlayProjection = this.getProjection();
// Retrieve the southwest and northeast coordinates of this overlay
// in latlngs and convert them to pixels 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 - 20) + 'px';
div.style.top = (ne.y - 20) + 'px';
div.style.width = 40 + 'px';
div.style.height = 40 + 'px';
};
var element = document.getElementById("map");
var map = new google.maps.Map(element, {
center: new google.maps.LatLng(48.705236, 9.128566), // Latitude/Breitengrad , Longitude/Längengrad
zoom: 18,
mapTypeId: google.maps.MapTypeId.ROADMAP, // Google maps provider
//mapTypeId: "OSM", // OpenStreetMap provider
mapTypeControl: false,
streetViewControl: false
});

HTML Canvas As Overlayview In Google Maps That Is Fixed Relative to the Map Canvas

I'm trying to create an HTML5 canvas as an OverlayView the size of a map, position it to top:0; left:0;, draw some stuff on it, and add it to the map. Whenever the map zooms or pans I want to remove the old canvas from the map and create a new canvas draw on it position it to 0,0 and add it to the map. However the map never reposition to top:0; left:0. Can someone help?
function CustomLayer(map){
this.latlngs = new Array();
this.map_ = map;
this.addMarker = function(position){
this.latlngs.push(position);
}
this.drawCanvas = function(){
this.setMap(this.map_);
//google.maps.event.addListener(this.map_, 'bounds_changed',this.reDraw());
}
}
function defineOverlay() {
CustomLayer.prototype = new google.maps.OverlayView();
CustomLayer.prototype.onAdd = function() {
console.log("onAdd()");
if(this.canvas){
var panes = this.getPanes();
panes.overlayLayer.appendChild(this.canvas);
}
}
CustomLayer.prototype.remove = function() {
console.log("onRemove()");
if(this.canvas)
this.canvas.parentNode.removeChild(this.canvas);
}
CustomLayer.prototype.draw = function() {
console.log("draw()");
this.remove();
this.canvas = document.createElement("canvas");
this.canvas.setAttribute('width', '800px');
this.canvas.setAttribute('height', '480px');
this.canvas.setAttribute('top', '30px');
this.canvas.setAttribute('left', '30px');
this.canvas.setAttribute('position', 'absolute');
this.canvas.setAttribute('border', '1px solid red');
this.canvas.style.border = '1px solid red';
//using this way for some reason scale up the images and mess up the positions of the markers
/*this.canvas.style.position = 'absolute';
this.canvas.style.top = '0px';
this.canvas.style.left = '0px';
this.canvas.style.width = '800px';
this.canvas.style.height = '480px';
this.canvas.style.border = '1px solid red';*/
//get the projection from this overlay
overlayProjection = this.getProjection();
//var mapproj = this.map_.getProjection();
if(this.canvas.getContext) {
var context = this.canvas.getContext('2d');
context.clearRect(0,0,800,480);
for(i=0; i<this.latlngs.length; i++){
p = overlayProjection.fromLatLngToDivPixel(this.latlngs[i]);
//p = mapproj.fromLatLngToPoint(this.latlngs[i]);
img = new Image();
img.src = "standardtick.png";
console.log(Math.floor(p.x)+","+Math.floor(p.y));
context.drawImage(img,p.x,p.y);
}
}
this.onAdd();
console.log("canvas width:"+this.canvas.width+" canvas height: "+this.canvas.height);
console.log("canvas top:"+this.canvas.getAttribute("top")+" left: "+this.canvas.getAttribute("left"));
}
}
In this example - I think it is important to draw attention to the difference between projection.fromLatLngToDivPixel and projection.fromLatLngToContainerPixel. In this context, DivPixel is used to keep the position of the canvas centered over the map view - while ContainerPixel is used to find the positions of the shapes you are drawing to the canvas.
What follows is a complete working example I worked out while playing around with this problem myself.
Required CSS Properties for the Overlay:
.GMAPS_OVERLAY
{
border-width: 0px;
border: none;
position:absolute;
padding:0px 0px 0px 0px;
margin:0px 0px 0px 0px;
}
Initialize the Map and Create a Test based on Google Markers
var mapsize = { width: 500, height: 500 };
var mapElement = document.getElementById("MAP");
mapElement.style.height = mapsize.width + "px";
mapElement.style.width = mapsize.width + "px";
var map = new google.maps.Map(document.getElementById("MAP"), {
mapTypeId: google.maps.MapTypeId.TERRAIN,
center: new google.maps.LatLng(0, 0),
zoom: 2
});
// Render G-Markers to Test Proper Canvas-Grid Alignment
for (var lng = -180; lng < 180; lng += 10)
{
var marker = new google.maps.Marker({
position: new google.maps.LatLng(0, lng),
map: map
});
}
Define the Custom Overlay
var CanvasOverlay = function(map) {
this.canvas = document.createElement("CANVAS");
this.canvas.className = "GMAPS_OVERLAY";
this.canvas.height = mapsize.height;
this.canvas.width = mapsize.width;
this.ctx = null;
this.map = map;
this.setMap(map);
};
CanvasOverlay.prototype = new google.maps.OverlayView();
CanvasOverlay.prototype.onAdd = function() {
this.getPanes().overlayLayer.appendChild(this.canvas);
this.ctx = this.canvas.getContext("2d");
this.draw();
};
CanvasOverlay.prototype.drawLine = function(p1, p2) {
this.ctx.beginPath();
this.ctx.moveTo( p1.x, p1.y );
this.ctx.lineTo( p2.x, p2.y );
this.ctx.closePath();
this.ctx.stroke();
};
CanvasOverlay.prototype.draw = function() {
var projection = this.getProjection();
// Shift the Canvas
var centerPoint = projection.fromLatLngToDivPixel(this.map.getCenter());
this.canvas.style.left = (centerPoint.x - mapsize.width / 2) + "px";
this.canvas.style.top = (centerPoint.y - mapsize.height / 2) + "px";
// Clear the Canvas
this.ctx.clearRect(0, 0, mapsize.width, mapsize.height);
// Draw Grid with Canvas
this.ctx.strokeStyle = "#000000";
for (var lng = -180; lng < 180; lng += 10)
{
this.drawLine(
projection.fromLatLngToContainerPixel(new google.maps.LatLng(-90, lng)),
projection.fromLatLngToContainerPixel(new google.maps.LatLng( 90, lng))
);
}
};
Initializing the Canvas
I find that I like to add an additional call to draw on the "dragend" event - but test it out to see what you think on your needs.
var customMapCanvas = new CanvasOverlay(map);
google.maps.event.addListener(map, "drawend", function() {
customMapCanvas.draw();
};
In cases where Canvas Drawing is Slowing Down Map
On the applications I work with, I find that the Map Framework calls the 'draw' method far too often on canvas's which are drawing something that takes a second or so to complete. In this case I define the 'draw' prototype function to be simply an empty function while naming my real draw function as 'canvasDraw' - then add event listeners for "zoomend" and "dragend". What you get here is a canvas that updates only after a user changes the zoom level or at the end of a map drag action.
CanvasOverlay.prototype.draw = function() { };
...
google.maps.event.addListener(map, "dragend", function() {
customMapCanvas.canvasDraw();
});
google.maps.event.addListener(map, "zoom_changed", function() {
customMapCanvas.canvasDraw();
});
Live Demo: Complete Example - All Inline Source
Once your map moves, the drawing context needs to know it has moved.
CustomOverlayView.prototype.alignDrawingPane = function(force) {
window.mapProjection = this.getProjection();
var center = window.mapProjection.fromLatLngToDivPixel(map.getCenter());
//My drawing container is dragged along with the map when panning
//this.drawPane refers to any node in MapPanes retrieved via this.getPanes()
this.drawPane.css({left:center.x - (mapWidth/2), top:center.y-(mapHeight/2)});
};
Call this in your draw() method. Make sure your draw gets called when you're done dragging:
google.maps.event.addListener(map, 'dragend', function() {
myCustomOverlay.draw();
});

Is it possible to write custom text on Google Maps API v3?

Is it possible to write a custom text on Google Maps API v3 next to the marker, or I can use only the info window to do that?
To show custom text you need to create a custom overlay. Below is an example adapted from official Google documentation. You could also use this library for more "stylish" info windows
<html>
<head>
<script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=false">
</script>
<script>
//adapded from this example http://code.google.com/apis/maps/documentation/javascript/overlays.html#CustomOverlays
//text overlays
function TxtOverlay(pos, txt, cls, map) {
// Now initialize all properties.
this.pos = pos;
this.txt_ = txt;
this.cls_ = cls;
this.map_ = map;
// We define a property to hold the image's
// div. We'll actually create this div
// upon receipt of the add() method so we'll
// leave it null for now.
this.div_ = null;
// Explicitly call setMap() on this overlay
this.setMap(map);
}
TxtOverlay.prototype = new google.maps.OverlayView();
TxtOverlay.prototype.onAdd = function() {
// Note: an overlay's receipt of onAdd() indicates that
// the map's panes are now available for attaching
// the overlay to the map via the DOM.
// Create the DIV and set some basic attributes.
var div = document.createElement('DIV');
div.className = this.cls_;
div.innerHTML = this.txt_;
// Set the overlay's div_ property to this DIV
this.div_ = div;
var overlayProjection = this.getProjection();
var position = overlayProjection.fromLatLngToDivPixel(this.pos);
div.style.left = position.x + 'px';
div.style.top = position.y + 'px';
// We add an overlay to a map via one of the map's panes.
var panes = this.getPanes();
panes.floatPane.appendChild(div);
}
TxtOverlay.prototype.draw = function() {
var overlayProjection = this.getProjection();
// Retrieve the southwest and northeast coordinates of this overlay
// in latlngs and convert them to pixels coordinates.
// We'll use these coordinates to resize the DIV.
var position = overlayProjection.fromLatLngToDivPixel(this.pos);
var div = this.div_;
div.style.left = position.x + 'px';
div.style.top = position.y + 'px';
}
//Optional: helper methods for removing and toggling the text overlay.
TxtOverlay.prototype.onRemove = function() {
this.div_.parentNode.removeChild(this.div_);
this.div_ = null;
}
TxtOverlay.prototype.hide = function() {
if (this.div_) {
this.div_.style.visibility = "hidden";
}
}
TxtOverlay.prototype.show = function() {
if (this.div_) {
this.div_.style.visibility = "visible";
}
}
TxtOverlay.prototype.toggle = function() {
if (this.div_) {
if (this.div_.style.visibility == "hidden") {
this.show();
} else {
this.hide();
}
}
}
TxtOverlay.prototype.toggleDOM = function() {
if (this.getMap()) {
this.setMap(null);
} else {
this.setMap(this.map_);
}
}
var map;
function init() {
var latlng = new google.maps.LatLng(37.9069, -122.0792);
var myOptions = {
zoom: 4,
center: latlng,
mapTypeId: google.maps.MapTypeId.ROADMAP
};
map = new google.maps.Map(document.getElementById("map"), myOptions);
var marker = new google.maps.Marker({
position: latlng,
map: map,
title: "Hello World!"
});
customTxt = "<div>Blah blah sdfsddddddddddddddd ddddddddddddddddddddd<ul><li>Blah 1<li>blah 2 </ul></div>"
txt = new TxtOverlay(latlng, customTxt, "customBox", map)
}
</script>
<style>
.customBox {
background: yellow;
border: 1px solid black;
position: absolute;
}
</style>
</head>
<body onload="init()">
<div id="map" style="width: 600px; height: 600px;">
</div>
</body>
</html>
By far the easiest way to add a Text Overlay is to use the MapLabel class from https://github.com/googlemaps/js-map-label
var mapLabel = new MapLabel({
text: 'Test',
position: new google.maps.LatLng(50,50),
map: map,
fontSize: 20,
align: 'right'
});
To add custom text to google maps, you can use a marker with an empty pixel for the icon:
new google.maps.Marker({
position: { lat: 0, lng: 0 },
map: map,
icon: '../res/img/empty.png',
label: {
color: '#FF0000',
fontWeight: 'bold',
text: 'Hello World',
fontSize: '20px',
},
});
Style options are listed here
It the text is static, you can use a marker and an image :
var label = new google.maps.Marker({
position: new google.maps.LatLng(50,50),
map: map,
icon: "/images/mytextasanimage.png"
});
The latest (v3) API recommends using async defer and a callback when the Maps API is loaded.
<script async defer src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&callback=initMap"></script>
To make this work you need to define the overlay class from within (or after) the initialization when the google class has been defined. Otherwise you will get google not defined errors.
function initMap() {
var map = new google.maps.Map(document.getElementById('map'), {
center: { lat: 40, lng: -30 },
zoom: 3
});
TxtOverlay.prototype = new google.maps.OverlayView();
var overlay = new TxtOverlay(new google.maps.LatLng(0, 0),
"<div>Have a wonderful overlay day</div>",
"customCSSClass",
map);
}
...
//adapded from answer above
function TxtOverlay(pos, txt, cls, map) {
// Now initialize all properties.
this.pos = pos;
this.txt_ = txt;
this.cls_ = cls;
this.map_ = map;
// We define a property to hold the image's
// div. We'll actually create this div
// upon receipt of the add() method so we'll
// leave it null for now.
this.div_ = null;
this.onAdd = function() {
// Note: an overlay's receipt of onAdd() indicates that
// the map's panes are now available for attaching
// the overlay to the map via the DOM.
// Create the DIV and set some basic attributes.
var div = document.createElement('DIV');
div.className = this.cls_;
div.innerHTML = this.txt_;
// Set the overlay's div_ property to this DIV
this.div_ = div;
var overlayProjection = this.getProjection();
var position = overlayProjection.fromLatLngToDivPixel(this.pos);
div.style.left = position.x + 'px';
div.style.top = position.y + 'px';
// We add an overlay to a map via one of the map's panes.
var panes = this.getPanes();
panes.floatPane.appendChild(div);
}
this.draw = function() {
var overlayProjection = this.getProjection();
// Retrieve the southwest and northeast coordinates of this overlay
// in latlngs and convert them to pixels coordinates.
// We'll use these coordinates to resize the DIV.
var position = overlayProjection.fromLatLngToDivPixel(this.pos);
var div = this.div_;
div.style.left = position.x + 'px';
div.style.top = position.y + 'px';
}
this.onRemove = function() {
this.div_.parentNode.removeChild(this.div_);
this.div_ = null;
}
this.hide = function() {
if (this.div_) {
this.div_.style.visibility = "hidden";
}
}
this.show = function() {
if (this.div_) {
this.div_.style.visibility = "visible";
}
}
this.toggle = function() {
if (this.div_) {
if (this.div_.style.visibility == "hidden") {
this.show();
} else {
this.hide();
}
}
}
this.toggleDOM = function() {
if (this.getMap()) {
this.setMap(null);
} else {
this.setMap(this.map_);
}
}
// Explicitly call setMap() on this overlay
this.setMap(map);
}
Here is working code:
#map {
height: 500px;
}
html, body {
height: 100%;
margin: 0;
padding: 0;
}
<div id="content">
<div id="map"></div>
<div class="centered">
<h1>Text Over Maps</h1>
</div>
</div>