Javascript 如何在谷歌地图api V3中偏移中心点
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/10656743/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to offset the center point in Google maps api V3
提问by will
I have a Google map with a semi transparent panel covering a portion of the area. I would like to adjust the center point of the map to take into account the portion of the map that is partially obscured. See the image below. Ideally, where the crosshairs and pin are placed would be the center point of the map.
我有一张谷歌地图,上面有一个半透明面板,覆盖了该区域的一部分。我想调整地图的中心点以考虑到部分被遮挡的地图部分。见下图。理想情况下,放置十字准线和图钉的位置将是地图的中心点。
I hope that makes sense.
我希望这是有道理的。
The reason is simple: When you zoom it needs to center the map over the crosshair rather than at 50% 50%. Also, I will be plotting markers on the map and moving through them in sequence. When the map centers on them, they also need to be at the offset position.
原因很简单:缩放时需要将地图居中放置在十字准线上,而不是 50% 50%。此外,我将在地图上绘制标记并按顺序移动它们。当地图以它们为中心时,它们也需要处于偏移位置。
Thanks in advance!
提前致谢!
回答by Andrew Leach
This is not particularly difficult once you find the relevant previous answer.
一旦您找到相关的先前答案,这并不是特别困难。
You need to convert the centre of the map to its world co-ordinates, find where the map needs to be centered to put the apparentcentre where you want it, and re-centre the map using the realcentre.
您需要将地图的中心转换为其世界坐标,找到需要将地图居中的位置以将明显的中心放在您想要的位置,然后使用真实中心重新居中地图。
The API will always centre the map on the centre of the viewport, so you need to be careful if you use map.getCenter()
as it will return the real centre, not the apparent centre. I suppose it would be possible to overload the API so that its getCenter()
and setCenter()
methods are replaced, but I haven't done that.
API 将始终将地图居中放置在视口的中心,因此您在使用时需要小心,map.getCenter()
因为它会返回真实的中心,而不是明显的中心。我想可以重载 API 以便替换它的getCenter()
和setCenter()
方法,但我还没有这样做。
Code below. Example online. In the example, clicking the button shifts the centre of the map (there's a road junction there) down 100px and left 200px.
代码如下。在线示例。在示例中,单击该按钮会将地图的中心(那里有一个路口)向下移动 100 像素并向左移动 200 像素。
function offsetCenter(latlng, offsetx, offsety) {
// latlng is the apparent centre-point
// offsetx is the distance you want that point to move to the right, in pixels
// offsety is the distance you want that point to move upwards, in pixels
// offset can be negative
// offsetx and offsety are both optional
var scale = Math.pow(2, map.getZoom());
var worldCoordinateCenter = map.getProjection().fromLatLngToPoint(latlng);
var pixelOffset = new google.maps.Point((offsetx/scale) || 0,(offsety/scale) ||0);
var worldCoordinateNewCenter = new google.maps.Point(
worldCoordinateCenter.x - pixelOffset.x,
worldCoordinateCenter.y + pixelOffset.y
);
var newCenter = map.getProjection().fromPointToLatLng(worldCoordinateNewCenter);
map.setCenter(newCenter);
}
回答by Kah Tang
Also take a look at the panBy(x:number, y:number) function on the maps object.
另请查看地图对象上的 panBy(x:number, y:number) 函数。
The documentation mentions this about the function:
文档提到了有关该功能的内容:
Changes the center of the map by the given distance in pixels. If the distance is less than both the width and height of the map, the transition will be smoothly animated. Note that the map coordinate system increases from west to east (for x values) and north to south (for y values).
按给定的像素距离更改地图的中心。如果距离小于地图的宽度和高度,过渡将平滑动画。请注意,地图坐标系从西向东(对于 x 值)和从北向南(对于 y 值)增加。
Just use it like this:
只需像这样使用它:
mapsObject.panBy(200, 100)
回答by brycewjohnson
Here's a simpler method that might be more useful in responsive design since you can use percentages instead of pixels. No world coordinates, no LatLngs to Points!
这是一种更简单的方法,在响应式设计中可能更有用,因为您可以使用百分比而不是像素。没有世界坐标,没有 LatLngs 到点!
var center; // a latLng
var offsetX = 0.25; // move center one quarter map width left
var offsetY = 0.25; // move center one quarter map height down
var span = map.getBounds().toSpan(); // a latLng - # of deg map spans
var newCenter = {
lat: center.lat() + span.lat()*offsetY,
lng: center.lng() + span.lng()*offsetX
};
map.panTo(newCenter); // or map.setCenter(newCenter);
回答by Konstantin Kalbazov
Here's an example of solving the problem using panBy()
method of the maps API: http://jsfiddle.net/upsidown/2wej9smf/
以下是使用panBy()
地图 API 的方法解决问题的示例:http: //jsfiddle.net/upsidown/2wej9smf/
回答by Nickensoul
Just found another simplest solution. In case you're using fitBounds method, you can pass optional second argument to its. This argument is padding, that will be considered while fitting bounds.
刚刚找到了另一个最简单的解决方案。如果您使用 fitBounds 方法,您可以将可选的第二个参数传递给它。这个参数是填充,将在拟合边界时考虑。
// pass single side:
map.fitBounds(bounds, { left: 1000 })
// OR use Number:
map.fitBounds(bounds, 20)
Further reading: official docs.
进一步阅读:官方文档。
回答by Nahuel
Andrew's is the answer. However, in my case, map.getBounds() kept returning undefined. I fixed it waiting for the bounds_changed event and thencall the function to offset the center. Like so:
安德鲁就是答案。但是,就我而言, map.getBounds() 一直返回未定义。我修复了它等待 bounds_changed 事件,然后调用函数来偏移中心。像这样:
var center_moved = false;
google.maps.event.addListener(map, 'bounds_changed', function() {
if(!center_moved){
offsetCenter(map.getCenter(), 250, -200);
center_moved = true;
}
});
回答by Mardoxx
Old question, I know. But how about a more CSS-centric way?
老问题,我知道。但是一种更以 CSS 为中心的方式呢?
http://codepen.io/eddyblair/pen/VjpNQQ
http://codepen.io/eddyblair/pen/VjpNQQ
What I did was:
我所做的是:
Wrap the map and overlay in a container with
overflow: hidden
Overlaid the overlay with
position: absolute
Extended the map's apparent widthby the overlay width (plus any padding and offset) by setting a negative
margin-left
.Then in order to comply with https://www.google.com/permissions/geoguidelines/attr-guide.htmlpositioned the widgets and attribution
div
s.
将地图和覆盖物包裹在一个容器中
overflow: hidden
覆盖覆盖物
position: absolute
通过设置一个负值,通过叠加宽度(加上任何填充和偏移)扩展地图的表观宽度
margin-left
。然后为了符合https://www.google.com/permissions/geoguidelines/attr-guide.html定位小部件和属性
div
。
This way the centre of the map lies in line with the centre of the desired area. The js is just standard map js.
这样,地图的中心就与所需区域的中心对齐。js 只是标准的地图 js。
Repositioning the icons for street-view is an exercise for the reader :)
重新定位街景图标是读者的练习:)
If you want the overlay on the left, just change line 24
margin-left
to margin-right
and line 32 right
to left
.
如果您想要左侧的叠加层,只需将 line 更改24
margin-left
为margin-right
line 32right
即可left
。
回答by Chris
After extensive searching I could not find a way to do this that also included zoom. Thankfully a clever chaphas figured it out. There is also a fiddle here
经过广泛的搜索,我找不到一种方法来做到这一点,也包括缩放。谢天谢地,一个聪明的家伙已经想通了。这里还有一个小提琴
'use strict';
const TILE_SIZE = {
height: 256,
width: 256
}; // google World tile size, as of v3.22
const ZOOM_MAX = 21; // max google maps zoom level, as of v3.22
const BUFFER = 15; // edge buffer for fitting markers within viewport bounds
const mapOptions = {
zoom: 14,
center: {
lat: 34.075328,
lng: -118.330432
},
options: {
mapTypeControl: false
}
};
const markers = [];
const mapDimensions = {};
const mapOffset = {
x: 0,
y: 0
};
const mapEl = document.getElementById('gmap');
const overlayEl = document.getElementById('overlay');
const gmap = new google.maps.Map(mapEl, mapOptions);
const updateMapDimensions = () => {
mapDimensions.height = mapEl.offsetHeight;
mapDimensions.width = mapEl.offsetWidth;
};
const getBoundsZoomLevel = (bounds, dimensions) => {
const latRadian = lat => {
let sin = Math.sin(lat * Math.PI / 180);
let radX2 = Math.log((1 + sin) / (1 - sin)) / 2;
return Math.max(Math.min(radX2, Math.PI), -Math.PI) / 2;
};
const zoom = (mapPx, worldPx, fraction) => {
return Math.floor(Math.log(mapPx / worldPx / fraction) / Math.LN2);
};
const ne = bounds.getNorthEast();
const sw = bounds.getSouthWest();
const latFraction = (latRadian(ne.lat()) - latRadian(sw.lat())) / Math.PI;
const lngDiff = ne.lng() - sw.lng();
const lngFraction = ((lngDiff < 0) ? (lngDiff + 360) : lngDiff) / 360;
const latZoom = zoom(dimensions.height, TILE_SIZE.height, latFraction);
const lngZoom = zoom(dimensions.width, TILE_SIZE.width, lngFraction);
return Math.min(latZoom, lngZoom, ZOOM_MAX);
};
const getBounds = locations => {
let northeastLat;
let northeastLong;
let southwestLat;
let southwestLong;
locations.forEach(function(location) {
if (!northeastLat) {
northeastLat = southwestLat = location.lat;
southwestLong = northeastLong = location.lng;
return;
}
if (location.lat > northeastLat) northeastLat = location.lat;
else if (location.lat < southwestLat) southwestLat = location.lat;
if (location.lng < northeastLong) northeastLong = location.lng;
else if (location.lng > southwestLong) southwestLong = location.lng;
});
const northeast = new google.maps.LatLng(northeastLat, northeastLong);
const southwest = new google.maps.LatLng(southwestLat, southwestLong);
const bounds = new google.maps.LatLngBounds();
bounds.extend(northeast);
bounds.extend(southwest);
return bounds;
};
const zoomWithOffset = shouldZoom => {
const currentzoom = gmap.getZoom();
const newzoom = shouldZoom ? currentzoom + 1 : currentzoom - 1;
const offset = {
x: shouldZoom ? -mapOffset.x / 4 : mapOffset.x / 2,
y: shouldZoom ? -mapOffset.y / 4 : mapOffset.y / 2
};
const newCenter = offsetLatLng(gmap.getCenter(), offset.x, offset.y);
if (shouldZoom) {
gmap.setZoom(newzoom);
gmap.panTo(newCenter);
} else {
gmap.setCenter(newCenter);
gmap.setZoom(newzoom);
}
};
const setMapBounds = locations => {
updateMapDimensions();
const bounds = getBounds(locations);
const dimensions = {
width: mapDimensions.width - mapOffset.x - BUFFER * 2,
height: mapDimensions.height - mapOffset.y - BUFFER * 2
};
const zoomLevel = getBoundsZoomLevel(bounds, dimensions);
gmap.setZoom(zoomLevel);
setOffsetCenter(bounds.getCenter());
};
const offsetLatLng = (latlng, offsetX, offsetY) => {
offsetX = offsetX || 0;
offsetY = offsetY || 0;
const scale = Math.pow(2, gmap.getZoom());
const point = gmap.getProjection().fromLatLngToPoint(latlng);
const pixelOffset = new google.maps.Point((offsetX / scale), (offsetY / scale));
const newPoint = new google.maps.Point(
point.x - pixelOffset.x,
point.y + pixelOffset.y
);
return gmap.getProjection().fromPointToLatLng(newPoint);
};
const setOffsetCenter = latlng => {
const newCenterLatLng = offsetLatLng(latlng, mapOffset.x / 2, mapOffset.y / 2);
gmap.panTo(newCenterLatLng);
};
const locations = [{
name: 'Wilshire Country Club',
lat: 34.077796,
lng: -118.331151
}, {
name: '301 N Rossmore Ave',
lat: 34.077146,
lng: -118.327805
}, {
name: '5920 Beverly Blvd',
lat: 34.070281,
lng: -118.331831
}];
locations.forEach(function(location) {
let marker = new google.maps.Marker({
position: new google.maps.LatLng(location.lat, location.lng),
title: location.name
})
marker.setMap(gmap);
markers.push(marker);
});
mapOffset.x = overlayEl.offsetWidth;
document.zoom = bool => zoomWithOffset(bool);
document.setBounds = () => setMapBounds(locations);
section {
height: 180px;
margin-bottom: 15px;
font-family: sans-serif;
color: grey;
}
figure {
position: relative;
margin: 0;
width: 100%;
height: 100%;
}
figcaption {
position: absolute;
left: 15px;
top: 15px;
width: 120px;
padding: 15px;
background: white;
box-shadow: 0 2px 5px rgba(0, 0, 0, .3);
}
gmap {
display: block;
height: 100%;
}
<script type="text/javascript" src="https://maps.googleapis.com/maps/api/js"></script>
<section>
<figure>
<gmap id="gmap"></gmap>
<figcaption id="overlay">
<h4>Tile Overlay</h4>
<p>To be avoided by the map!</p>
</figcaption>
</figure>
</section>
<button onclick="zoom(true)">zoom in</button>
<button onclick="zoom(false)">zoom out</button>
<button onclick="setBounds()">set bounds</button>
回答by MrUpsidown
Another approach when it comes to offsetting a route or a group of markers can be found here:
可以在此处找到偏移路线或一组标记的另一种方法:
https://stackoverflow.com/a/26192440/1238965
https://stackoverflow.com/a/26192440/1238965
It still uses the fromLatLngToPoint()
method described in @Andrew Leach answer.
它仍然使用fromLatLngToPoint()
@Andrew Leach 回答中描述的方法。