Setting maximum and minimum bounds using Google Maps API - google-maps

Was wondering if someone could provide an example of how to set maximum and minimum extents using GMaps2? In OpenLayers, here's how it is done:
var point1 = new OpenLayers.Geometry.Point(7, 48);
point1.transform(new OpenLayers.Projection("EPSG:4326"), new OpenLayers.Projection("EPSG:900913"));
var point2 = new OpenLayers.Geometry.Point(11, 54);
point2.transform(new OpenLayers.Projection("EPSG:4326"), new OpenLayers.Projection("EPSG:900913"));
var bounds = new OpenLayers.Bounds();
bounds.extend(point1);
bounds.extend(point2);
bounds.toBBOX();
map = new OpenLayers.Map("map", {
maxExtent: bounds,
maxResolution:"auto",
maxZoomLevel: 8,
projection:"EPSG:900913",
controls: []
});
map.addLayer(new OpenLayers.Layer.OSM.Osmarender("Osmarender"));
map.zoomToMaxExtent();
//map.zoomToExtent(bounds);
What would be the GMaps2 equivalent?

The following is the Google Maps API 2.x equivalent to limit the panning of the map to predefined bounds:
// Bounds for North America
var allowedBounds = new GLatLngBounds(new GLatLng(48.197218, -127.529297),
new GLatLng(28.72913, -68.818359));
function checkBounds() {
if (allowedBounds.contains(map.getCenter())) {
return;
}
var c = map.getCenter();
var x = c.lng();
var y = c.lat();
var maxX = allowedBounds.getNorthEast().lng();
var maxY = allowedBounds.getNorthEast().lat();
var minX = allowedBounds.getSouthWest().lng();
var minY = allowedBounds.getSouthWest().lat();
if (x < minX) {x = minX;}
if (x > maxX) {x = maxX;}
if (y < minY) {y = minY;}
if (y > maxY) {y = maxY;}
map.setCenter(new GLatLng(y, x));
}
GEvent.addListener(map, "move", function() { checkBounds(); });

Thanks for getting back to me Chris and Daniel. What I meant by 'extents' is that when zoomed out is maximized, the world map doesn't tile. I don't want to see 2, 3 or even 4 North America's and other continents. The world is big enough with just one. When I zoom in, I want to set a limit to how far one can zoom in to account for our data resolution. Zoom in too far returns results outside our data resolution. I did manage to find an acceptable solution by restricting the zoom levels. Anyway, FWIW, here's what I came up. It's a bit less verbose but isn't doing exactly what Daniel does in his code:
//===== Restrict Zoom Levels =====
var mapTypes = map.getMapTypes();
// Overwrite the getMinimumResolution() and getMaximumResolution() methods
for (var i=0; i<mapTypes.length; i++) {
mapTypes[i].getMinimumResolution = function() {return 2;}
mapTypes[i].getMaximumResolution = function() {return 9;}
}
I still want to try Daniel's code to see what it does. Special thanks to you for taking the time to write this up Dan.
-=Al

Related

How can I adjust the zoom on Open Layers for viewing all POI and Markers on the map

I need to show all POI and Markers in a Open Layers map with the best zoom defined.
How can I get that?
I need the best zoom to show all markers on view.
function setMapCenter() {
var layers = multiMapa.getLayers();
//that's it. It's started inverted to be adjusted on first iteration below.
var minx = 9999999999;
var miny = 9999999999;
var maxx = -9999999999;
var maxy = -9999999999;
var extent = [];
layers.forEach(function (lay) {
if (lay instanceof ol.layer.Vector) {
vehicleLayer = lay;
var features = lay.getSource().getFeatures();
features.forEach(function (fea) {
//all vectors, in this case, are Points.
var coords = String(fea.getGeometry().getExtent()).split(",");
if (minx > coords[0]) {
minx = coords[0];
}
if (miny > coords[1]) {
miny = coords[1];
}
if (maxx < coords[0]) {
maxx = coords[0];
}
if (maxy < coords[1]) {
maxy = coords[1];
}
});
//console.log(lay.getStyle().getText().getText());
}
});
console.log([minx, miny, maxx, maxy]);
extent.push(minx);
extent.push(miny);
extent.push(maxx);
extent.push(maxy);
multiMapa.getView().fit(extent, multiMapa.getSize()); //[minx, miny, maxx, maxy]);
}
If all your features are on the same layer, get your vector's extent like;
const extent = yourLayer.getExtent();
Then do this;
map.getView().fit(extent);
If they are on separate layers, you can either jam all of the features in a separate layer and get the extent that way or you can combine all of the features extents like this;
let featExtent = featuresOfInterest[0].getGeometry().getExtent();
for (let i = 0; i < featuresOfInterest.length; i++) {
ol.extent.extend(featExtent, featuresOfInterest[i].getGeometry().getExtent());
}
Then use the fit function of the view again like;
map.getView().fit(featExtent);

Prevent map jumping/swapping hozirontally using Google Map API's groundoverlay

All,
I am new to Google Map API. I am trying to display some images over Google Maps. I want to use GroundOverlay because it is easier to implement animation effects later in my work.
The problem is the image will jump horizontally between 'world-tiles' especially when I drag the map crossing -180 deg and 180 deg. Only part of the screen shows the image, which is annoying. Here's a screenshot:
Does anybody know how to prevent this? Actually I would like to replicate the behavior at:
http://forevermore.net/articles/photo-zoom/
I guess if I can limit the left and right boundaries of the map, then the image will not warp horizontally. But the example showing above is based on the tile overlay not GroudOverlay. Any ideas to implement the behavior using GroundOverlay?
You can try using this:
google.maps.event.addListener(map,'center_changed',function() { checkBounds(); });
var allowedBounds = new google.maps.LatLngBounds(
new google.maps.LatLng( , ), //add your lat/long
new google.maps.LatLng( , ) //add your lat/long
);
function checkBounds() {
if(! allowedBounds.contains(map.getCenter())) {
var C = map.getCenter();
var X = C.lng();
var Y = C.lat();
var maxX = allowedBounds.getNorthEast().lng();
var maxY = allowedBounds.getNorthEast().lat();
var minX = allowedBounds.getSouthWest().lng();
var minY = allowedBounds.getSouthWest().lat();
if (X < minX) {X = minX;}
if (X > maxX) {X = maxX;}
if (Y < minY) {Y = minY;}
if (Y > maxY) {Y = maxY;}
map.setCenter(new google.maps.LatLng(Y,X));
}
}

Google Maps specific part

I am working on Google maps V3 and i want to get a specific part of Google map like i want to display only Pakistan on Google map and other map should hide completely .?
You probably can't hide other map parts completely but you can set boundaries to the map so that the user cannot scroll any further than the defined point
google.maps.event.addListener(map,'center_changed',function() { checkBounds(); });
function checkBounds() {
if(! allowedBounds.contains(map.getCenter())) {
var C = map.getCenter();
var X = C.lng();
var Y = C.lat();
var AmaxX = allowedBounds.getNorthEast().lng();
var AmaxY = allowedBounds.getNorthEast().lat();
var AminX = allowedBounds.getSouthWest().lng();
var AminY = allowedBounds.getSouthWest().lat();
if (X < AminX) {X = AminX;}
if (X > AmaxX) {X = AmaxX;}
if (Y < AminY) {Y = AminY;}
if (Y > AmaxY) {Y = AmaxY;}
map.setCenter(new google.maps.LatLng(Y,X));
}
}

Multiple google markers on the same place

i'm trying to use google maps with markers. i do not have any problems with the placement of markers in the map, but how can i get the markers to split like google earth when i have to markers in the same place? like this : Example
Thanks !
I didn't understand what you're trying to accomplish, but ...
Did you already check a marker clustering algorithm like this one or the google semi-official ?
//Here is my attempt... a Archimedes spiraling out of the markers:
// calc a spiraling out position based on marker count at that location
// this function is very tweeky
function spiral_coords(lat_long, i) {
i = (i == 1)? 0: i+1;
var r = i * 0.002;
// .8 is a fudge number to adjust to real appearance on the map
return [lat_long[0] + (r * .8 * Math.sin(.5 * (i + 2))), lat_long[1] + (r * Math.cos(.5 * (i + 2)))];
}
// this is from a fusion table query... but your source could be anything
// I take the coords and check against a hash count of them and calc out the spiral position
function data_handler(d) {
var map = $("#map")[0];
map.markers = [];
var rows = d.rows;
var fields = d.columns;
var index = {};
for (var i in fields) {
index[fields[i]] = i;
}
var location_count = {};
for (var i in rows) {
var row = rows[i];
var location = row[index["Location"]];
var lat_long = location.split(" ");
lat_long[0] = parseFloat(lat_long[0]);
lat_long[1] = parseFloat(lat_long[1]);
// here are the active ingredients
if(!(location in location_count)) {
location_count[location] = 0;
}
location_count[location]++;
lat_long = spiral_coords(lat_long, location_count[location]);
var marker = new google.maps.Marker({
position: new google.maps.LatLng(lat_long[0], lat_long[1]),
map: map.map
});
}
}

Google Maps API V3: limit map bounds [duplicate]

This question already has answers here:
How do I limit panning in Google maps API V3?
(15 answers)
Closed 8 years ago.
I'm trying to set bounds where you can drag the map using Google Maps API V3
Here is the solution for V2 http://econym.org.uk/gmap/example_range.htm that works pretty well.
However with API V3 it isn't so good: when you use the same checkbounds() function, the map is twitching when you reach the bound while map.setCenter() changes the center of the map.
How to fix it? What is the solution for API V3?
I've had the same problem, but this should sort it out (it's the same function, the event to listen to is changed from 'move' or 'drag' to 'center_changed', works like a charm!:
google.maps.event.addListener(map,'center_changed',function() { checkBounds(); });
function checkBounds() {
if(! allowedBounds.contains(map.getCenter())) {
var C = map.getCenter();
var X = C.lng();
var Y = C.lat();
var AmaxX = allowedBounds.getNorthEast().lng();
var AmaxY = allowedBounds.getNorthEast().lat();
var AminX = allowedBounds.getSouthWest().lng();
var AminY = allowedBounds.getSouthWest().lat();
if (X < AminX) {X = AminX;}
if (X > AmaxX) {X = AmaxX;}
if (Y < AminY) {Y = AminY;}
if (Y > AmaxY) {Y = AmaxY;}
map.setCenter(new google.maps.LatLng(Y,X));
}
}
You might also need to consider wrapping coordinates, curve distortion, and centerizing to the bound dimensions if the map resizes or zooms in/out. This is especially required if your bounds takes up a large percentage of the entire map (eg. like a continent).
One of the problems with checkBounds() though, is that it doesn't take into account that latitude values close to the north/south poles, which have non-linear distortion which make limiting the bounds in-accurate (I use approximate magic number multipliers that won't work in all situations). By right, you should first convert the bounds to linear 2d world coordinates to see how far off the bounds it is in terms of world coordinates, than map the actual target center point in world coordinate to the target actual latitude position. For longitude values, this doesn't seem like much of issue and the linear clipping approach seems accurate enough, the main issue is with the wrapping of longitude coordinates which is accounted for (somewhat) in the below code.
// Persitant variables
var allowedBounds; // assign something here
var lastValidCenter; // initialize this using map.getCenter()
function checkBounds() { // when bounds changes due to resizing or zooming in/out
var currentBounds = map.getBounds();
if (currentBounds == null) return;
var allowed_ne_lng = allowedBounds.getNorthEast().lng();
var allowed_ne_lat = allowedBounds.getNorthEast().lat();
var allowed_sw_lng = allowedBounds.getSouthWest().lng();
var allowed_sw_lat = allowedBounds.getSouthWest().lat();
var wrap;
var cc = map.getCenter();
var centerH = false;
var centerV = false;
// Check horizontal wraps and offsets
if ( currentBounds.toSpan().lng() > allowedBounds.toSpan().lng() ) {
centerH = true;
}
else { // test positive and negative wrap respectively
wrap = currentBounds.getNorthEast().lng() < cc.lng();
var current_ne_lng = !wrap ? currentBounds.getNorthEast().lng() : allowed_ne_lng +(currentBounds.getNorthEast().lng() + 180 ) + (180 - allowed_ne_lng);
wrap = currentBounds.getSouthWest().lng() > cc.lng();
var current_sw_lng = !wrap ? currentBounds.getSouthWest().lng() : allowed_sw_lng - (180-currentBounds.getSouthWest().lng()) - (allowed_sw_lng+180);
}
// Check vertical wraps and offsets
if ( currentBounds.toSpan().lat() > allowedBounds.toSpan().lat() ) {
centerV = true;
}
else { // test positive and negative wrap respectively
wrap = currentBounds.getNorthEast().lat() < cc.lat(); if (wrap) { alert("WRAp detected top") } // else alert("no wrap:"+currentBounds); wrap = false;
var current_ne_lat = !wrap ? currentBounds.getNorthEast().lat() : allowed_ne_lat + (currentBounds.getNorthEast().lat() +90) + (90 - allowed_ne_lat);
wrap = currentBounds.getSouthWest().lat() > cc.lat(); if (wrap) { alert("WRAp detected btm") } //alert("no wrap:"+currentBounds);
var current_sw_lat = !wrap ? currentBounds.getSouthWest().lat() : allowed_sw_lat - (90-currentBounds.getSouthWest().lat()) - (allowed_sw_lat+90);
}
// Finalise positions
var centerX = cc.lng();
var centerY = cc.lat();
if (!centerH) {
if (current_ne_lng > allowed_ne_lng) centerX -= current_ne_lng-allowed_ne_lng;
if (current_sw_lng < allowed_sw_lng) centerX += allowed_sw_lng-current_sw_lng;
}
else {
centerX = allowedBounds.getCenter().lng();
}
if (!centerV) {
if (current_ne_lat > allowed_ne_lat) {
centerY -= (current_ne_lat-allowed_ne_lat) * 3; // approximation magic numbeer. Adjust as u see fit, or use a more accruate pixel measurement.
}
if (current_sw_lat < allowed_sw_lat) {
centerY += (allowed_sw_lat-current_sw_lat)*2.8; // approximation magic number
}
}
else {
centerY = allowedBounds.getCenter().lat();
}
map.setCenter(lastValidCenter = new google.maps.LatLng(centerY,centerX));
}
function limitBound(bound) // Occurs during dragging, pass allowedBounds to this function in most cases. Requires persistant 'lastValidCenter=map.getCenter()' var reference.
{
var mapBounds = map.getBounds();
if ( mapBounds.getNorthEast().lng() >= mapBounds.getSouthWest().lng() && mapBounds.getNorthEast().lat() >= mapBounds.getSouthWest().lat() // ensure no left/right, top/bottom wrapping
&& bound.getNorthEast().lat() > mapBounds.getNorthEast().lat() // top
&& bound.getNorthEast().lng() > mapBounds.getNorthEast().lng() // right
&& bound.getSouthWest().lat() < mapBounds.getSouthWest().lat() // bottom
&& bound.getSouthWest().lng() < mapBounds.getSouthWest().lng()) // left
{
lastValidCenter=map.getCenter(); // valid case, set up new valid center location
}
// if (bound.contains(map.getCenter()))
// {
map.panTo(lastValidCenter);
// }
}
// Google map listeners
google.maps.event.addListener(map, 'zoom_changed', function() {
//var zoom = map.getZoom();
checkBounds();
});
google.maps.event.addListener(map, "bounds_changed", function() {
checkBounds();
});
google.maps.event.addListener(map, 'center_changed', function() {
limitBound(allowedBounds);
});
p.s For checkBounds(), to get proper 2d world coordinate from the map's center, given 2 lat/lng values, use map.getProjection().fromLatLngToPoint(). Compare the 2 points, find the linear difference between them, and map the difference in world coordinates back to lat/lng using map.getProjection().fromPointToLatLng(). This will give you accurate clip offsets in lat/lng units.
This script gets the initial bounds (allowedBounds) and limit the bounds on drag and zoom_changed. Also the zoom is limited on < 7.
var allowedBounds = false;
google.maps.event.addListener(map, 'idle', function() {
if (!allowedBounds) {
allowedBounds = map.getBounds();
}
});
google.maps.event.addListener(map, 'drag', checkBounds);
google.maps.event.addListener(map, 'zoom_changed', checkBounds);
function checkBounds() {
if (map.getZoom() < 7) map.setZoom(7);
if (allowedBounds) {
var allowed_ne_lng = allowedBounds.getNorthEast().lng();
var allowed_ne_lat = allowedBounds.getNorthEast().lat();
var allowed_sw_lng = allowedBounds.getSouthWest().lng();
var allowed_sw_lat = allowedBounds.getSouthWest().lat();
var currentBounds = map.getBounds();
var current_ne_lng = currentBounds.getNorthEast().lng();
var current_ne_lat = currentBounds.getNorthEast().lat();
var current_sw_lng = currentBounds.getSouthWest().lng();
var current_sw_lat = currentBounds.getSouthWest().lat();
var currentCenter = map.getCenter();
var centerX = currentCenter.lng();
var centerY = currentCenter.lat();
if (current_ne_lng > allowed_ne_lng) centerX = centerX-(current_ne_lng-allowed_ne_lng);
if (current_ne_lat > allowed_ne_lat) centerY = centerY-(current_ne_lat-allowed_ne_lat);
if (current_sw_lng < allowed_sw_lng) centerX = centerX+(allowed_sw_lng-current_sw_lng);
if (current_sw_lat < allowed_sw_lat) centerY = centerY+(allowed_sw_lat-current_sw_lat);
map.setCenter(new google.maps.LatLng(centerY,centerX));
}
}
Thanks #sairafi. Your answer got me very close. I was getting an error where getBounds was undefined, so I wrapped it in another listener to make sure that the map was fully loaded first.
google.maps.event.addListenerOnce(map, 'tilesloaded', function() {
allowedBounds = map.getBounds();
google.maps.event.addListener(map,'center_changed',function() { checkBounds(allowedBounds); });
});
// Limit map area
function checkBounds(allowedBounds) {
if(!allowedBounds.contains(map.getCenter())) {
var C = map.getCenter();
var X = C.lng();
var Y = C.lat();
var AmaxX = allowedBounds.getNorthEast().lng();
var AmaxY = allowedBounds.getNorthEast().lat();
var AminX = allowedBounds.getSouthWest().lng();
var AminY = allowedBounds.getSouthWest().lat();
if (X < AminX) {X = AminX;}
if (X > AmaxX) {X = AmaxX;}
if (Y < AminY) {Y = AminY;}
if (Y > AmaxY) {Y = AmaxY;}
map.setCenter(new google.maps.LatLng(Y,X));
}
}
southWest = new google.maps.LatLng(48.59475380744011,22.247364044189453);
northEast = new google.maps.LatLng(48.655344320891444,22.352420806884766);
var limBound = new google.maps.LatLngBounds(southWest,northEast);
var lastCenter;
var option = {zoom:15,
center: limBound.getCenter(),
mapTypeId: google.maps.MapTypeId.ROADMAP};
var map = new google.maps.Map(document.getElementById('divMap'),option);
google.maps.event.addListener(map,'zoom_changed', function() {
minZoom(15);
});
google.maps.event.addListener(map,'drag',function(e){
limitBound(limBound);
});
function minZoom(minZoom){
if (map.getZoom()<minZoom)
{map.setZoom(minZoom);}
};
function limitBound(bound)
{
if (bound.getNorthEast().lat() > map.getBounds().getNorthEast().lat()
&& bound.getNorthEast().lng() > map.getBounds().getNorthEast().lng()
&& bound.getSouthWest().lat() < map.getBounds().getSouthWest().lat()
&& bound.getSouthWest().lng() < map.getBounds().getSouthWest().lng())
{
lastCenter=map.getCenter();
$('#divText').text(lastCenter.toString());
}
if (bound.contains(map.getCenter()))
{
map.setCenter(lastCenter);
}
}
pls check this Google Maps API v3: Can I setZoom after fitBounds?
map.fitBounds(mapBounds);
#sairafi and #devin
thanks for both your answers. I could not get this to work in Chrome/Windows 7 because the .comtains() check evlauated to both true and false as soon as you hit the boundary.
So I changed the setCenter() at the bottom to panTo()
The second problem was that if you establish boundaries on initial load, you have to use the google.maps.event.addListenerOnce(map,'idle'...) event otherwise it keeps resetting the boundaries to the currently viewable map.
Finally I do use the drag event for tracking centers, for some reason that worked more smoothly.
The resulting code is this:
google.maps.event.addListenerOnce(map,'idle',function() {
allowedBounds = map.getBounds();
});
google.maps.event.addListener(map,'drag',function() {
checkBounds();
});
function checkBounds() {
if(! allowedBounds.contains(map.getCenter()))
{
var C = map.getCenter();
var X = C.lng();
var Y = C.lat();
var AmaxX = allowedBounds.getNorthEast().lng();
var AmaxY = allowedBounds.getNorthEast().lat();
var AminX = allowedBounds.getSouthWest().lng();
var AminY = allowedBounds.getSouthWest().lat();
if (X < AminX) {X = AminX;}
if (X > AmaxX) {X = AmaxX;}
if (Y < AminY) {Y = AminY;}
if (Y > AmaxY) {Y = AmaxY;}
map.panTo(new google.maps.LatLng(Y,X));
}
}