Google Maps API V3: Offset panTo() by x pixels - google-maps-api-3

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);
}

Related

How to overlay a raster image type with Google Maps API?

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.

OpenLayers: Registering function to marker event does not work

I have a map in OpenLayers with a simple layer and a marker layer.
The markers in the marker layer are generated from data.rows. For every marker, I want to register a function to the "mousedown" (or "click") event, but that does not seem to work. The markers got added to the map, but upon clicking, the function registered to the event is not entered.
// Marker-Layer
var markers = new OpenLayers.Layer.Markers("SABA")
// Icon
var size = new OpenLayers.Size(21, 25);
var offset = new OpenLayers.Pixel(-(size.w / 2), -size.h);
var iconPath = document.location.protocol + '//' + document.location.hostname + '/saba/modulesinst/sa/icons/pin.png';
var icon = new OpenLayers.Icon(iconPath, size, offset);
// Rows durchgehen
Array.each(data.rows, function(item, index) {
if (item.x != null && item.y != null) {
var newmarker = new OpenLayers.Marker(new OpenLayers.LonLat(item.x, item.y), icon.clone())
newmarker.events.register('mousedown', newmarker, function(evt) {
alert(item.name);
OpenLayers.Event.stop(evt);
});
markers.addMarker(newmarker);
}
});
this.listMap.addLayer(markers);
The Openlayers Documentation states that you should use a vector layer for this purpose instead of a marker layer: "Markers are the ‘older’ way to interact with geographic data in the browser. Most new code should, where possible, use vector layers in place of marker layers".
When you use a vector layer you can add markers like this:
var marker = new OpenLayers.Feature.Vector(new OpenLayers.Geometry.Point(lon,lat), attributes);
vectorLayer.addFeatures(marker);
and you can use this code to initialize the vector layer:
function selected (evt) {
alert(evt.feature.id + " selected on " + this.name);
}
var layer = new OpenLayes.Layer.Vector("VLayer");
layer.events.register("featureselected", layer, selected);
and finally this to add the select feature control to your map:
var control = new OpenLayers.Control.SelectFeature(layer);
map.addControl(control);
control.activate();
Refer to this Openlayers Documentation

(NaN, NaN) when getting latitude and longitude through GET

I'm getting (NaN, NaN) when I trying to fetch the latitude and longitude information from an GET in JavaScript. Here's my code:
var GET = {};
var params = location.search.substr(1).split("&");
for (var i=0; i < params.length; i++) {
var par = params[i].split('=');
GET[par[0]] = par[1];
}
function initialize() {
var latitude_longitude = new google.maps.LatLng(GET['coordinates']);
// The basic code to call Google Maps JavaScript API v3 ...
}
Do I have to put the latitude and longitude in clear text (for example ... new google.maps.LatLng(59.328614,13.485847))?
Thanks in advance.
LatLng() takes two arguments, so you will need to split the coordinates:
function initialize() {
var csplit = GET['coordinates'].split(',');
var latitude_longitude = new google.maps.LatLng(csplit[0], csplit[1]);
// The basic code to call Google Maps JavaScript API v3 ...
}
Can you elaborate, what's stored in coordinates?
You might have to use split(','), then write new google.maps.LatLng(parseFloat(value[0]),parseFloat(value[1]))

Flex Flashbuilder Google Maps

Hi I'm wondering if anybody could help me with a problem I'm trying to solve with Flex & google maps.
I have a map that is populated by markers. Each marker has an event listener. And what I am hoping to achieve is that when each marker is clicked that a datagrid is populated with the data associated to that marker. However at the moment I can only populate the data grid with the LatLng object. I need to find a way to access the other data associated with that Marker.
Here is my event listener:
private function createMarker(latlng:LatLng, int:Number, tip:String, desc:String):Marker
{
var m:Marker = new Marker (latlng, new MarkerOptions ({hasShadow: true, tooltip: "" +tip}));
m.addEventListener(MapMouseEvent.CLICK, function(event:MapMouseEvent):void
{details.addItem(event.latLng.toString());});
return m;
}
I was thinking it might be along the lines of getitem where LatLng = event.latLng but I'm really new to flex so I can't figure it out at all.
Any ideas that might put me on the right track would be really appreciated.
L
Try this:
private var markerArray:Array = [];
private var markerDescriptsArray:Array = [];
private function createMarker(latlng:LatLng, int:Number, tip:String, desc:String):Marker
{
var m:Marker = new Marker (latlng, new MarkerOptions ({hasShadow: true, tooltip: "" +tip}));
m.addEventListener(MapMouseEvent.CLICK, function(event:MapMouseEvent):void
{
var _mIndex:uint;
for (var i:uint = 0; i < markerArray.length; i++)
{
if(markerArray[i] == Marker(e.currentTarget)) _mIndex = i;
}
details.addItem(markerDescriptsArray[_mIndex]);
});
markerArray.push(m);
markerDescriptsArray.push(desc);
return m;
}

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