GMap Polyline position of center - google-maps

When I create Gmap she needed to establish the center and zoom
Once we've created a map via the GMap2
constructor, we need to initialize it.
This initialization is accomplished
with use of the map's setCenter ()
method. The setCenter () method
requires a GLatLng coordinate and a
zoom level and this method must be
sent before any other operations are
performed on the map, including
setting any other attributes of the
map itself.
Because of this route is not positioned at the center - this is an example http://grab.by/4OD6
I should on the basis of the coordinates to get the center?
And get a zoom which displays all objects map?
My code:
var TrainingGMap = Class.create ((
initialize: function (div_id, points, options) (
this.options = Object.extend ((), options)
this.points = points;
this.map = new GMap2 (document.getElementById (div_id));
this.map.setCenter (new GLatLng (this.points [0]. lan, this.points [0]. lon), 12);
this.map.setUIToDefault ();
this.set_route ();
)
set_route: function () (
var line = new Array ();
for (var i = 0; i <this.points.length; i + +) (
line [i] = new GLatLng (this.points [i]. lat, this.points [i]. lon);
)
var polyline = new GPolyline (line, "# aa0000", 5);
this.map.addOverlay (polyline);
)
));

I resolve my problem
this.map.setCenter(polyline.getBounds().getCenter());
this.map.setZoom(this.map.getBoundsZoomLevel(polyline.getBounds()));
New code
var TrainingGMap = Class.create({
initialize: function(div_id, points, options) {
this.options = Object.extend({}, options)
this.points = points;
this.map = new GMap2(document.getElementById(div_id));
this.map.setCenter(new GLatLng(this.points[0].lan, this.points[0].lon), 12);
this.map.setUIToDefault();
var line = new Array();
for (var i = 0; i < this.points.length; i++) {
line[i] = new GLatLng(this.points[i].lan,this.points[i].lon);
}
var polyline = new GPolyline(line, "#aa0000", 5);
this.map.setCenter(polyline.getBounds().getCenter());
this.map.setZoom(this.map.getBoundsZoomLevel(polyline.getBounds()));
this.map.addOverlay(polyline);
}
});

var TrainingGMap = Class.create({
initialize: function(div_id, points, options) {
this.options = Object.extend({}, options)
this.points = points;
this.map = new GMap2(document.getElementById(div_id));
this.map.setCenter(new GLatLng(this.points[0].lan, this.points[0].lon), 12);
this.map.setUIToDefault();
var line = new Array();
for (var i = 0; i < this.points.length; i++) {
line[i] = new GLatLng(this.points[i].lan,this.points[i].lon);
}
var polyline = new GPolyline(line, "#aa0000", 5);
var tengah=Math.round(polyline.getVertexCount()/2)-1;
this.map.setCenter(polyline.getVertex(tengah));
this.map.setZoom(this.map.getBoundsZoomLevel(polyline.getBounds()));
this.map.addOverlay(polyline);
}
});

Related

How to get the terrain height at mouse position when in 2D map?

I'm using Cesium in 2D mode.
This is the code I'm using to take the terrain height at mouse position:
cartographic = Cesium.Ellipsoid.WGS84.cartesianToCartographic( position );
var longitudeString = Cesium.Math.toDegrees(cartographic.longitude).toFixed(10);
var latitudeString = Cesium.Math.toDegrees(cartographic.latitude).toFixed(10);
mapPointerLatitude = latitudeString.slice(-15);
mapPointerLongitude = longitudeString.slice(-15);
var tempHeight = cartographic.height;
if( tempHeight < 0 ) tempHeight = 0;
mapPointerHeight = tempHeight.toFixed(2);
Where position came from a Cesium.ScreenSpaceEventType.MOUSE_MOVE event:
if ( mapStyle === '2D' ) {
var position = viewer.camera.pickEllipsoid(movement.endPosition, scene.globe.ellipsoid);
if (position) {
return position;
}
}
if ( mapStyle === '3D' ) {
var ray = viewer.camera.getPickRay(movement.endPosition);
var position = viewer.scene.globe.pick(ray, viewer.scene);
if (Cesium.defined(position)) {
return position;
}
}
When the map is in 3D mode (Cesium.SceneMode.SCENE3D) I have the height value in tempHeight but when the map is in Cesium.SceneMode.SCENE2D this value is always zero. What I'm doing wrong?
EDIT
Don't know if this is the best way but I get it to work putting my code inside this:
cartographic = Cesium.Ellipsoid.WGS84.cartesianToCartographic( position );
var positions = [ cartographic ];
var promise = Cesium.sampleTerrain(terrainProvider, 11, positions);
Cesium.when(promise, function( updatedPositions ) {
// PUT ALL HERE
});
You can actually try Cesium.sampleTerrainMostDetailed to get more precise altitude.
// Query the terrain height of two Cartographic positions
const terrainProvider = Cesium.createWorldTerrain();
const positions = [
Cesium.Cartographic.fromDegrees(86.925145, 27.988257),
Cesium.Cartographic.fromDegrees(87.0, 28.0)
];
Cesium.sampleTerrainMostDetailed(terrainProvider, positions).then((updatedPositions) => {
// positions[0].height and positions[1].height have been updated.
// updatedPositions is just a reference to positions.
})
https://cesium.com/learn/cesiumjs/ref-doc/global.html?classFilter=sample#sampleTerrainMostDetailed

He's dead, Jim: is there a memory limit on the number of circles that can be drawn on a Google map?

On a map of the USA, I have been asked to draw 50,000 circles each with a 5000-yard radius.
The lat-lon locations are scattered throughout the country, but a large number of these circles overlap. Opacity is set to 0.05; regions with many superimposed circles become more opaque.
The circles start to appear, but after about 30 seconds Chrome crashes, displaying the "He's dead, Jim" message.
About the error message:
According to Error: "He's Dead, Jim!":
You might see the “He’s Dead, Jim!” message in a tab if:
You don’t have enough memory available to run the tab. Computers rely on memory to run apps, extensions, and programs. Low memory
can cause them to run slowly or stop working.
You stopped a process using Google Chrome's Task Manager, the system's task manager, or a command line tool.
It evidently occurs since you are trying to render 50k objects. In order to render such amount of objects I would recommend to consider Overlay approach. In that case the performance could be improved considerably since city icons could ve rendered via canvas element instead of div ones.
Having said that, the below example demonstrates how to render multiple amount of cities (1000 cities ) using the described approach:
var overlay;
USCitiesOverlay.prototype = new google.maps.OverlayView();
function USCitiesOverlay(map) {
this._map = map;
this._cities = [];
this._radius = 6;
this._container = document.createElement("div");
this._container.id = "citieslayer";
this.setMap(map);
this.addCity = function (lat, lng) {
this._cities.push({position: new google.maps.LatLng(lat,lng)});
};
}
USCitiesOverlay.prototype.createCityIcon = function (id,pos) {
/*var cityIcon = document.createElement('div');
cityIcon.setAttribute('id', "cityicon_" + id);
cityIcon.style.position = 'absolute';
cityIcon.style.left = (pos.x - this._radius) + 'px';
cityIcon.style.top = (pos.y - this._radius) + 'px';
cityIcon.style.width = cityIcon.style.height = (this._radius * 2) + 'px';
cityIcon.className = "circleBase city";
return cityIcon;*/
var cityIcon = document.createElement('canvas');
cityIcon.id = 'cityicon_' + id;
cityIcon.width = cityIcon.height = this._radius * 2;
cityIcon.style.width = cityIcon.width + 'px';
cityIcon.style.height = cityIcon.height + 'px';
cityIcon.style.left = (pos.x - this._radius) + 'px';
cityIcon.style.top = (pos.y - this._radius) + 'px';
cityIcon.style.position = "absolute";
var centerX = cityIcon.width / 2;
var centerY = cityIcon.height / 2;
var ctx = cityIcon.getContext('2d');
ctx.fillStyle = 'rgba(160,16,0,0.6)';
ctx.beginPath();
ctx.arc(centerX, centerY, this._radius, 0, Math.PI * 2, true);
ctx.fill();
return cityIcon;
};
USCitiesOverlay.prototype.ensureCityIcon = function (id,pos) {
var cityIcon = document.getElementById("cityicon_" + id);
if(cityIcon){
cityIcon.style.left = (pos.x - this._radius) + 'px';
cityIcon.style.top = (pos.y - this._radius) + 'px';
return cityIcon;
}
return this.createCityIcon(id,pos);
};
USCitiesOverlay.prototype.onAdd = function () {
var panes = this.getPanes();
panes.overlayLayer.appendChild(this._container);
};
USCitiesOverlay.prototype.draw = function () {
var zoom = this._map.getZoom();
var overlayProjection = this.getProjection();
var container = this._container;
this._cities.forEach(function(city,idx){
var xy = overlayProjection.fromLatLngToDivPixel(city.position);
var cityIcon = overlay.ensureCityIcon(idx,xy);
container.appendChild(cityIcon);
});
};
USCitiesOverlay.prototype.onRemove = function () {
this._container.parentNode.removeChild(this._container);
this._container = null;
};
function getRandomInterval(min, max) {
return Math.random() * (max - min) + min;
}
function generateCityMap(count) {
var citymap = [];
var minPos = new google.maps.LatLng(49.25, -123.1);
var maxPos = new google.maps.LatLng(34.052234, -74.005973);
for(var i = 0; i < count;i++)
{
var lat = getRandomInterval(minPos.lat(),maxPos.lat());
var lng = getRandomInterval(minPos.lng(),maxPos.lng());
var population = getRandomInterval(10000,1000000);
citymap.push({
location: new google.maps.LatLng(lat, lng),
population: population
});
}
return citymap;
}
function initialize() {
var mapOptions = {
zoom: 4,
center: new google.maps.LatLng(37.09024, -95.712891),
mapTypeId: google.maps.MapTypeId.TERRAIN
};
var map = new google.maps.Map(document.getElementById('map-canvas'), mapOptions);
overlay = new USCitiesOverlay(map);
overlay.addCity(40.714352, -74.005973); //chicago
overlay.addCity(40.714352, -74.005973); //newyork
overlay.addCity(34.052234, -118.243684); //losangeles
overlay.addCity(49.25, -123.1); //vancouver
var citymap = generateCityMap(1000);
citymap.forEach(function(city){
overlay.addCity(city.location.lat(), city.location.lng());
});
}
google.maps.event.addDomListener(window, 'load', initialize);
html, body, #map-canvas {
height: 100%;
margin: 0px;
padding: 0px;
}
<script src="https://maps.googleapis.com/maps/api/js?v=3.exp&signed_in=true"></script>
<div id="map-canvas"></div>

How do I draw two parallel poly lines based on only two points (similar to google maps traffic)

I have a Polyline that is drawn from point A to point B, I'm trying to figure out how to draw two lines of a constant width that appear something like this image.
I only have two points, rather than four, I thought maybe there was a way to create four points, based on the two and then draw the corresponding lines, but I cant seem to get my head around it.
Any suggestions
I am currently trying to achieve the same thing, have two parallel polylines, but with different colors.
Have you seen this:
http://wtp2.appspot.com/ParallelLines.htm
It uses Google Maps v2 API, but it seems quite simple to convert it to API v3.
That's really complicated I think, because you have to re-calculate the route of the lines on every zoom-change.
When you don't need 2 different colors you could easy accomplish a similar effect by using the same route for two lines:
http://jsfiddle.net/doktormolle/KFsDB/
for google maps V3 if anyone is still interested
you can use this script which is modification on v2
// Parallel Polylines
// http://matthewschwartz.me/parallel-lines-and-google-maps-v3/
// Original Google Maps V2 awesome code by: Bill Chadwick March 2008
// Released as Free for any use # http://wtp2.appspot.com/ParallelLines.htm
//
// Modified for use with GMaps V3 by: Matthew Schwartz (schwartz.matthew#schwartzlink.net)
// Also released as free for any use
// Modified again for use by Jereme Causing. Fixed some errors and added parameters -June 2013
/*
* BDCCParallelLines(Array points<lat,lng>, String color, float weight, float opacity, float gap, String _type)
* _type can be either polyline or polygon. polyline is the default
* returns OverlayView();
*/
function BDCCParallelLines(points, color, weight, opacity, gapPx, _type, _editable) {
this.gapPx = gapPx;
this.points = points;
this.color = color;
this.weight = weight;
this.opacity = opacity;
this.prj = null;
this.line1 = null;
this.line2 = null;
this.zoomListener = null;
this._type = _type;
this._editable = false;
this.polygon = null;
if(_editable){
this._editable = _editable; //boolean
}
}
BDCCParallelLines.prototype = new google.maps.OverlayView();
// BDCCParallelLines implements the OverlayView interface
// Methods that need to be implemented in GMaps 3 = onAdd(), draw(), and onRemove()
BDCCParallelLines.prototype.onAdd = function() {
this.setProjection();
var foo = this;
var zoomRecalc = function() {
foo.onRemove();
foo.setProjection();
};
this.zoomListener = google.maps.event.addListener(this.map, 'zoom_changed', zoomRecalc);
}
BDCCParallelLines.prototype.setProjection = function() {
this.map = this.getMap();
var overlay = new google.maps.OverlayView();
overlay.draw = function() {};
overlay.setMap(this.map);
this.prj = overlay.getProjection();
}
BDCCParallelLines.prototype.onRemove = function() {
if(this.line2) {
this.line2.setMap(null);
this.line2 = null;
}
if(this.line1) {
this.line1.setMap(null);
this.line1 = null;
}
if (this.prj) {
this.prj = null;
}
if(this.zoomListener != null) {
google.maps.event.removeListener(this.zoomListener);
}
if(this.polygon){
this.polygon.setMap(null);
}
}
BDCCParallelLines.prototype.draw = function(map) {
if(this.line2) {
this.line2.setMap(null);
this.line2 = null;
}
if(this.line1) {
this.line1.setMap(null);
this.line1 = null;
}
this.recalc();
return;
}
BDCCParallelLines.prototype.redraw = function(force) {
return; //do nothing
}
BDCCParallelLines.prototype.recalc = function() {
// var zoom = this.map.getZoom();
var zoom = this.map.getZoom();
//left and right swapped throughout!
var pts1 = new Array();//left side of center
var pts2 = new Array();//right side of center
//shift the pts array away from the centre-line by half the gap + half the line width
var o = (this.gapPx + this.weight)/2;
var p2l,p2r;
for (var i=1; i<this.points.length; i++){
var p1lm1;
var p1rm1;
var p2lm1;
var p2rm1;
var thetam1;
var p1 = this.prj.fromLatLngToContainerPixel(this.points[i-1]);
var p2 = this.prj.fromLatLngToContainerPixel(this.points[i]);
var theta = Math.atan2(p1.x-p2.x,p1.y-p2.y) + (Math.PI/2);
var dl = Math.sqrt(((p1.x-p2.x)*(p1.x-p2.x))+((p1.y-p2.y)*(p1.y-p2.y)));
if(theta > Math.PI)
theta -= Math.PI*2;
var dx = Math.round(o * Math.sin(theta));
var dy = Math.round(o * Math.cos(theta));
var p1l = new google.maps.Point(p1.x+dx,p1.y+dy);
var p1r = new google.maps.Point(p1.x-dx,p1.y-dy);
p2l = new google.maps.Point(p2.x+dx,p2.y+dy);
p2r = new google.maps.Point(p2.x-dx,p2.y-dy);
if(i==1){ //first point
pts1.push(this.prj.fromContainerPixelToLatLng(p1l));
pts2.push(this.prj.fromContainerPixelToLatLng(p1r));
}
else{ // mid points
if(theta == thetam1){
// adjacent segments in a straight line
pts1.push(this.prj.fromContainerPixelToLatLng(p1l));
pts2.push(this.prj.fromContainerPixelToLatLng(p1r));
}
else{
var pli = this.intersect(p1lm1,p2lm1,p1l,p2l);
var pri = this.intersect(p1rm1,p2rm1,p1r,p2r);
var dlxi = (pli.x-p1.x);
var dlyi = (pli.y-p1.y);
var drxi = (pri.x-p1.x);
var dryi = (pri.y-p1.y);
var di = Math.sqrt((drxi*drxi)+(dryi*dryi));
var s = o / di;
var dTheta = theta - thetam1;
if(dTheta < (Math.PI*2))
dTheta += Math.PI*2;
if(dTheta > (Math.PI*2))
dTheta -= Math.PI*2;
if(dTheta < Math.PI){
//intersect point on outside bend
pts1.push(this.prj.fromContainerPixelToLatLng(p2lm1));
pts1.push(this.prj.fromContainerPixelToLatLng(new google.maps.Point(p1.x+(s*dlxi),p1.y+(s*dlyi)),zoom));
pts1.push(this.prj.fromContainerPixelToLatLng(p1l));
}
else if (di < dl){
pts1.push(this.prj.fromContainerPixelToLatLng(pli));
}
else{
pts1.push(this.prj.fromContainerPixelToLatLng(p2lm1));
pts1.push(this.prj.fromContainerPixelToLatLng(p1l));
}
var dxi = (pri.x-p1.x)*(pri.x-p1.x);
var dyi = (pri.y-p1.y)*(pri.y-p1.y);
if(dTheta > Math.PI){
//intersect point on outside bend
pts2.push(this.prj.fromContainerPixelToLatLng(p2rm1));
pts2.push(this.prj.fromContainerPixelToLatLng(new google.maps.Point(p1.x+(s*drxi),p1.y+(s*dryi)),zoom));
pts2.push(this.prj.fromContainerPixelToLatLng(p1r));
}
else if(di<dl)
pts2.push(this.prj.fromContainerPixelToLatLng(pri));
else{
pts2.push(this.prj.fromContainerPixelToLatLng(p2rm1));
pts2.push(this.prj.fromContainerPixelToLatLng(p1r));
}
}
}
p1lm1 = p1l;
p1rm1 = p1r;
p2lm1 = p2l;
p2rm1 = p2r;
thetam1 = theta;
}
if(this._type == 'polyline' || this._type == null) //default
{
pts1.push(this.prj.fromContainerPixelToLatLng(p2l));//final point
pts2.push(this.prj.fromContainerPixelToLatLng(p2r));
if(this.line1)
this.line1.setMap(null);
this.line1 = new google.maps.Polyline({
path: pts1,
strokeColor: this.color,
strokeOpacity: this.opacity,
strokeWeight: this.weight,
editable: this._editable
});
this.line1.setMap(this.map);
if(this.line2)
this.line1.setMap(null);
this.line2 = new google.maps.Polyline({
path: pts2,
strokeColor: this.color,
strokeOpacity: this.opacity,
strokeWeight: this.weight
});
this.line2.setMap(this.map);
}else if(this._type == 'polygon' ){
pts1.push(this.prj.fromContainerPixelToLatLng(p2l));//final point
pts2.push(this.prj.fromContainerPixelToLatLng(p2r));
var newpts = pts1.concat(pts2.reverse());
if(this.polygon){
this.polygon.setMap(null);
}
this.polygon = new google.maps.Polygon({
paths: newpts,
editable: true,
strokeColor: this.color,
fillColor: this.color,
strokeOpacity: this.opacity,
strokeWeight: this.weight,
editable: this._editable
});
this.polygon.setMap(this.map);
/* if(this.line2)
this.line1.setMap(null);
this.line2 = new google.maps.Polyline({
path: pts2,
strokeColor: this.color,
strokeOpacity: this.opacity,
strokeWeight: this.weight
});
this.line2.setMap(this.map);
*/
}
}
BDCCParallelLines.prototype.intersect = function(p0,p1,p2,p3)
{
// this function computes the intersection of the sent lines p0-p1 and p2-p3
// and returns the intersection point,
var a1,b1,c1, // constants of linear equations
a2,b2,c2,
det_inv, // the inverse of the determinant of the coefficient matrix
m1,m2; // the slopes of each line
var x0 = p0.x;
var y0 = p0.y;
var x1 = p1.x;
var y1 = p1.y;
var x2 = p2.x;
var y2 = p2.y;
var x3 = p3.x;
var y3 = p3.y;
// compute slopes, note the cludge for infinity, however, this will
// be close enough
if ((x1-x0)!==0)
m1 = (y1-y0)/(x1-x0);
else
m1 = 1e+10; // close enough to infinity
if ((x3-x2)!==0)
m2 = (y3-y2)/(x3-x2);
else
m2 = 1e+10; // close enough to infinity
// compute constants
a1 = m1;
a2 = m2;
b1 = -1;
b2 = -1;
c1 = (y0-m1*x0);
c2 = (y2-m2*x2);
// compute the inverse of the determinate
det_inv = 1/(a1*b2 - a2*b1);
// use Kramers rule to compute xi and yi
var xi=((b1*c2 - b2*c1)*det_inv);
var yi=((a2*c1 - a1*c2)*det_inv);
return new google.maps.Point(Math.round(xi),Math.round(yi));
}
as fount on this on this jsfiddle parrallel drawing

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