How to overlay a raster image type with Google Maps API? - google-maps-api-3

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:
external jpeg
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.

Related

Resize Raster according to screen size in PaperJS

I would like resize a Raster (image) when I resize the canvas via PaperJS. I'm using the following codes:
var last_point = view.center;
var img = new Raster('images/test.png', view.center);
img.onLoad = function() {
resizeImg();
}
function onResize(event) {
view.scrollBy(last_point.subtract(view.center));
last_point = view.center;
resizeImg();
}
function resizeImg() {
var width = paper.view.size.width;
var scale = (width / img.width) * 0.75;
img.scale(scale);
}
However, once going through resizeImg() function, the image is gone (commented the function out will make the image re-appear).
What did I miss in the function? I guess it's just a simple mistake in calculation
An object's width is referenced by its bounds.width property, not width alone.
function resizeImg() {
var width = paper.view.size.width;
var scale = (width / img.bounds.width) * 0.75;
img.scale(scale);
}
The following preserves the aspect ratio of the image and spares you the trouble of doing math stuff. fitBounds (doc) and view.onFrame (doc) make it all work automatically.
…
function resizeImg () {
img.fitBounds(view.bounds)
}
view.onFrame = function (event) {
resizeImg()
}

Custom map overlay heremaps js api v3

I am trying to switch a project from here maps 2.5.4 to 3.0.5.
I have a map with a custom animated image overlay. In 2.5.4 it is realized via ImageProvider:
var imageProvider = new nokia.maps.map.provider.ImageProvider({
opacity: 0.8,
getBoundingBox: function() {
return new nokia.maps.geo.BoundingBox(
new nokia.maps.geo.Coordinate(55.599073, 3.550307),
new nokia.maps.geo.Coordinate(47.27036232672, 15.434621365086)
)},
getUrl: function() {
return images[index]; // return the current image
},
updateCycle: 86400,
cache: new nokia.maps.util.Cache(100)
});
//add listener to show next image
imageProvider.addListener("update", function(evt) {
index++;
}
In v3.0.5 there is no ImageProvider and I didn't find another solution in the api documentation. Does anyone know how to realize it in v3 ?
Yup, there seems to be no ImageProvider (yet?).
You can hack it in, though:
// assuming you have a H.Map instance call "map"
// that's where we want the image
var rect = new H.geo.Rect(51, 12, 54, 15),
// that's the images we want
images = [
'http://newnation.sg/wp-content/uploads/random-pic-internet-06.jpg',
'http://newnation.sg/wp-content/uploads/random-pic-internet-04.jpg',
'http://newnation.sg/wp-content/uploads/random-pic-internet-06.jpg'
],
current = 0,
// that the image node we'll use
image = document.createElement('img');
// you could probably use CSS3 matrix transforms to improve performance
// but for demo purposes, I'lluse position:absolute and top/left for the image
image.style.position = "absolute";
image.style.opacity = "0.8";
// this function updates the image whenever something changes
var update = function() {
// project the rectangle's geo-coords to screen space
var topLeft = map.geoToScreen(rect.getTopLeft());
var bottomRight = map.geoToScreen(rect.getBottomRight());
// calculate top/left and width/height
var offsetX = topLeft.x;
var offsetY = topLeft.y;
var width = bottomRight.x - topLeft.x;
var height = bottomRight.y - topLeft.y;
// set image source (update is also called, when we choose another image)
image.src = images[current];
// set image position and size
image.style.top = offsetY + "px";
image.style.left = offsetX + "px";
image.style.width = width + "px";
image.style.height = height + "px";
};
// append the image
map.getViewPort().element.appendChild(image);
// set initial values
update();
// update whenever viewport or viewmodel changes
map.getViewPort().addEventListener('sync', function() {
update();
});
map.getViewModel().addEventListener('sync', function() {
update();
});
// zoom to rectangle (just to get the images nicely in view)
map.setViewBounds(rect);
// start the image change interval
setInterval(function() {
current = (current + 1) % 3;
update();
}, 3000);

google map marker image sprite

This is my first post here, hope someone can help.
I am building a mapping website using google maps.
The map shows upto 14 different icons based on a type category
I understand the theory of sprite images and the reduction in server calls these have so I have one image conating all the markers.
Question:
How do I set the position of the sprite, but only call the image once?
I have seen this post
Google map marker sprite image position
but that only shows changing the image for one marker
This is the code
//set the image once
var img = {
url:'images/site/type_sprite.png',
size: new google.maps.Size(32,32),
origin: new google.maps.Point(0,0)
};
idx = 0;
for (var i = 0; i < l; i++) {
lat = parseFloat(markers[i].getAttribute("lt"));
lng = parseFloat(markers[i].getAttribute("ln"));
var type = markers[i].getAttribute("typ");
if ((lat < latUpper && lat > latLower) && (lng < lngUpper && lng > lngLower) && (jQuery.inArray(type,filter_arr)>=0)) {
//change the position of the icon sprite depending on type
switch(type) {
case 'AA':
img(origin, new google.maps.Point(0,0));
break;
case 'AC':
img(origin = new google.maps.Point(0, 42));
break;
case 'ACF':
img(origin= new google.maps.Point(0,84));
break;
default:
img(origin= new google.maps.Point(0,0));
}
var id = markers[i].getAttribute("id");
var name = markers[i].getAttribute("nme");
//var icon = 'images/pins/' + type + '.png';
var point = new google.maps.LatLng(lat, lng);
var marker = new google.maps.Marker({
map: map,
position: point,
title: name,
icon: img
});
//add to valid array
markersArray.push(marker);
//build infowindow string
str = '<div class="iw_h">' + id + ' ' + name + '</div>';
bindInfoWindow(marker, map, infoWindow, str);
bounds.extend(point);
map.fitBounds(bounds);
idx++;
}
} //end of loop
}
If I put the code
var img = {
url:'images/site/type_sprite.png',
size: new google.maps.Size(32,32),
origin: new google.maps.Point(0,0)
};
inside the loop, doesn't that mean the image is repeatedly downloaded (and it will be a bigger image than an individual icon) thus defeating the object of using a sprite?
This is the site btw
http://www.searchforsites.co.uk/full_screen.php

Google Maps API V3: Offset panTo() by x pixels

I have a some UI elements on the right of my map (sometimes), and I'd like to offset my panTo() calls (sometimes).
So I figured:
get the original latlng
convert it to screen pixels
add an offset
convert it back to latlng.
But I must misunderstand what Google Maps API refers to as the "Point Plane":
http://code.google.com/apis/maps/documentation/javascript/reference.html#Projection
Here is my code that seems to offset by lat-long:
function getCentreOffset( alatlng ) {
var PIXEL_OFFSET= 100;
var aPoint = me.gmap.getProjection().fromLatLngToPoint(alatlng);
aPoint.x=aPoint.x + OFFSET;
return me.gmap.getProjection().fromPointToLatLng(aPoint);
}
Here's a simpler version of Ashley's solution:
google.maps.Map.prototype.panToWithOffset = function(latlng, offsetX, offsetY) {
var map = this;
var ov = new google.maps.OverlayView();
ov.onAdd = function() {
var proj = this.getProjection();
var aPoint = proj.fromLatLngToContainerPixel(latlng);
aPoint.x = aPoint.x+offsetX;
aPoint.y = aPoint.y+offsetY;
map.panTo(proj.fromContainerPixelToLatLng(aPoint));
};
ov.draw = function() {};
ov.setMap(this);
};
You can then use it like this:
var latlng = new google.maps.LatLng(-34.397, 150.644);
var map = new google.maps.Map(document.getElementById("map_canvas"), {
zoom: 8,
mapTypeId: google.maps.MapTypeId.ROADMAP,
center: latlng
});
setTimeout(function() { map.panToWithOffset(latlng, 0, 150); }, 1000);
Here is a working example.
Let me explain in detail. This extends the Map object itself. So you can use it just like panTo() with extra parameters for offsets. This uses the fromLatLngToContainerPixel() and fromContainerPixelToLatLng() methods of the MapCanvasProjecton class. This object has no contructor and has to be gotten from the getProjection() method of the OverlayView class; the OverlayView class is used for the creation of custom overlays by implementing its interface, but here we just use it directly. Because getProjection() is only available after onAdd() has been called. The draw() method is called after onAdd() and is defined for our instance of OverlayView to be a function that does nothing. Not doing so will otherwise cause an error.
Answer by Dean looks a lot cleaner as said in some comments, but was looking a little complicated to me. This single line solution is looking more elegant to me.
var map = $('#map_canvas').gmap3("get")
map.panBy(-500,-500); // (x,y)
Set center of map first. Then panyBy will shift the center in (x,y) direction. The more negative x, map will shift right. The more negative y, map will shift down.
Ok I found the answer here: How to call fromLatLngToDivPixel in Google Maps API V3?
First create function/prototpe to access the map's projection (difficult in V3)
//declare function/prototpe
function CanvasProjectionOverlay() {}
//define..
CanvasProjectionOverlay.prototype = new google.maps.OverlayView();
CanvasProjectionOverlay.prototype.constructor = CanvasProjectionOverlay;
CanvasProjectionOverlay.prototype.onAdd = function(){};
CanvasProjectionOverlay.prototype.draw = function(){};
CanvasProjectionOverlay.prototype.onRemove = function(){};
var gmap;
var canvasProjectionOverlay;
var PIXEL_OFFSET= 100;
function showUluru(isOffset=false){
//create map
var gmap = new google.maps.Map($('#map_canvas', {});
//create projection
canvasProjectionOverlay = new CanvasProjectionOverlay();
canvasProjectionOverlay.setMap(gmap);
var uluruRock = new google.maps.LatLng(-25.335448,135.745076);
if (isOffset)
uluruRock = getCentreOffset(uluruRock);
gmap.panTo( uluruRock )
}
//Use this function on LatLng you want to PanTo();
function getCentreOffset( alatlng ) {
var proj = canvasProjectionOverlay.getProjection();
var aPoint = proj.fromLatLngToContainerPixel(alatlng);
aPoint.x=aPoint.x+PIXEL_OFFSET;
return proj.fromContainerPixelToLatLng(aPoint);
}

get boundaries longitude and latitude from current zoom google maps

i need to know long and lat of my four corners of current area
as in this image
i have tried this but with no luck :
map.getBounds();
any help ?
You are half way there. All you need to do is to get the map bounds and then extract (and properly use) the coordinates of the corners.
var bounds = map.getBounds();
var ne = bounds.getNorthEast(); // LatLng of the north-east corner
var sw = bounds.getSouthWest(); // LatLng of the south-west corder
You get north-west and south-east corners from the two above:
var nw = new google.maps.LatLng(ne.lat(), sw.lng());
var se = new google.maps.LatLng(sw.lat(), ne.lng());
Just keep in mind that the map has to be already initialized, otherwise the map bounds are null or undefined.
If you want to be updated about every change of the viewport, use idle event listener:
google.maps.event.addListener(map, 'idle', function(ev){
// update the coordinates here
});
In Android, you can find out corner LatLng (northeast, southwest) by this way,
in the map, every time the camera listen(means gesture detect) they are called,
private var mapView: GoogleMap? = null
............
mapView?.setOnCameraMoveStartedListener { reasonCode ->
if (reasonCode == GoogleMap.OnCameraMoveStartedListener.REASON_GESTURE) {
val screenView: LatLngBounds = mapView.getProjection().getVisibleRegion().latLngBounds
var northeast=screenView.northeast
var southwest=screenView.southwest
var center=screenView.center
Log.v("northeast LatLng","-:"+northeast)// LatLng of the north-east Screen
Log.v("southwest LatLng","-:"+southwest)// LatLng of the south-west Screen
Log.v("center LatLng","-:"+center)// LatLng of the center Screen
}
}
**this is mapbox zoom example**
map.on('zoom', (el) => {
map.getBounds();
});

Resources