I've got a Google Maps API on my web site like this:
map = new google.maps.Map(document.getElementById("map"),
{
tilt:0
,mapTypeId: google.maps.MapTypeId.SATELLITE
,mapTypeControlOptions: {
mapTypeIds: ["satellite", "terrain","roadmap"],
style: google.maps.MapTypeControlStyle.DROPDOWN_MENU,
position: google.maps.ControlPosition.TOP_CENTER
}
}
);
I would like to add a new raster overlay to this map. This is the external overlay that I would like to add:
As you can see, there are 6 parameters that compose the URL to get the JPEG that I would like to overlay to Google Maps:
https://it-it.topographic-map.com/
?_path=api.maps.getOverlay
&southLatitude=43.40402777777778
&westLongitude=12.41375
&northLatitude=43.47263888888889
&eastLongitude=12.613194444444446
&zoom=13
&version=202211041528
4 parameters (Southlat, Westlng, Nordlat and Eastlng) that are the bounds of the image, the zoom level and version.
I would like to have some suggestions to write a JS function to add this layer to my Google Maps instance.
I tried looking at maptype image overlay example but I can see that there are only 2 coordinates and not 4, and I cannot understand how tiles works, and if this is compatible with my external image source.
The Topographic-map.com service indeed needs the 4 lat/lng coordinates and the zoom level so you can get an image of whatever dimensions you need.
Therefore the easiest method would be to use a Custom Overlay.
Based off the example linked above, you can create a map, get its bounds, calculate the 4 coordinates (and the zoom level) you need to request the image.
The idea is that you need to recreate the overlay with the new bounds and zoom level every time the user pans the map or zooms in/out, which is why I have used the idle
map event listener in the below example.
Also with such a solution, it will work whatever the map size / aspect ratio.
let map;
let overlay = {};
function initMap() {
const mapOptions = {
center: new google.maps.LatLng(46.102284, 7.357985),
zoom: 8,
mapTypeId: google.maps.MapTypeId.ROADMAP
};
map = new google.maps.Map(document.getElementById("map-canvas"), mapOptions);
// Listen for idle map event
google.maps.event.addListener(map, "idle", function() {
// Check if overlay already exists
if (overlay.hasOwnProperty('map')) {
// Remove overlay
overlay.setMap(null)
}
// Get the map bounds and needed coordinates and zoom level
let bounds = map.getBounds();
let southLat = bounds.getSouthWest().lat();
let northLat = bounds.getNorthEast().lat();
let eastLng = bounds.getNorthEast().lng();
let westLng = bounds.getSouthWest().lng();
let zoom = map.getZoom();
// Build the image URL
let srcImg = `https://it-it.topographic-map.com/?_path=api.maps.getOverlay&southLatitude=${southLat}&westLongitude=${westLng}&northLatitude=${northLat}&eastLongitude=${eastLng}&zoom=${zoom}&version=202211041528`;
// Create the overlay
overlay = new TopoOverlay(bounds, srcImg, map);
});
TopoOverlay.prototype = new google.maps.OverlayView();
}
/** @constructor */
function TopoOverlay(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.
*/
TopoOverlay.prototype.onAdd = function() {
let 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.
let 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.
let panes = this.getPanes();
//panes.overlayLayer.appendChild(div);
this.getPanes().overlayMouseTarget.appendChild(div);
};
TopoOverlay.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.
let 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.
let sw = overlayProjection.fromLatLngToDivPixel(this.bounds_.getSouthWest());
let ne = overlayProjection.fromLatLngToDivPixel(this.bounds_.getNorthEast());
// Resize the image's div to fit the indicated dimensions.
let 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';
div.style.opacity = 0.75;
};
// The onRemove() method will be called automatically from the API if
// we ever set the overlay's map property to 'null'.
TopoOverlay.prototype.onRemove = function() {
this.div_.parentNode.removeChild(this.div_);
this.div_ = null;
};
#map-canvas {
height: 180px;
}
<div id="map-canvas"></div>
<script defer src="//maps.googleapis.com/maps/api/js?key=AIzaSyCkUOdZ5y7hMm0yrcCQoCvLwzdM6M8s5qk&callback=initMap"></script>
It doesn't work in the snippet because of Uncaught DOMException: Blocked a frame with origin "null" from accessing a cross-origin frame
.
Not sure why I get this.
Working code here in a JSFiddle.