繁体   English   中英

如何在谷歌地图api V3中偏移中心点

[英]How to offset the center point in Google maps api V3

我有一张谷歌地图,上面有一个半透明面板,覆盖了该区域的一部分。 我想调整地图的中心点以考虑到部分被遮挡的地图部分。 见下图。 理想情况下,放置十字准线和图钉的位置将是地图的中心点。

我希望这是有道理的。

原因很简单:缩放时需要将地图居中放置在十字准线上,而不是 50% 50%。 此外,我将在地图上绘制标记并按顺序移动它们。 当地图以它们为中心时,它们也需要处于偏移位置。

提前致谢!

我正在构建的地图模型

一旦您找到相关的先前答案,这并不是特别困难。

您需要将地图的中心转换为其世界坐标,找到需要将地图居中的位置以将明显的中心放在您想要的位置,然后使用真实中心重新居中地图。

API 将始终将地图以视口的中心为中心,因此在使用map.getCenter()需要小心,因为它将返回真实中心,而不是明显中心。 我想可以重载 API 以替换其getCenter()setCenter()方法,但我还没有这样做。

代码如下。 在线示例 在示例中,单击该按钮会将地图的中心(那里有一个路口)向下移动 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);

}

另请查看地图对象上的 panBy(x:number, y:number) 函数。

文档提到了有关该功能的内容:

按给定的像素距离更改地图的中心。 如果距离小于地图的宽度和高度,过渡将平滑动画。 请注意,地图坐标系从西向东(对于 x 值)和从北向南(对于 y 值)增加。

只需像这样使用它:

mapsObject.panBy(200, 100)

这是使用地图 API 的panBy()方法解决问题的示例: http : //jsfiddle.net/upsidown/2wej9smf/

这是一种更简单的方法,在响应式设计中可能更有用,因为您可以使用百分比而不是像素。 没有世界坐标,没有 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);

刚刚找到了另一个最简单的解决方案。 如果您使用 fitBounds 方法,您可以将可选的第二个参数传递给它。 这个参数是填充,将在拟合边界时考虑。

// pass single side:
map.fitBounds(bounds, { left: 1000 })

// OR use Number:
map.fitBounds(bounds, 20)

进一步阅读: 官方文档

安德鲁的就是答案。 但是,就我而言, 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; 
  }
});

老问题,我知道。 但是一种更以 CSS 为中心的方式呢?

http://codepen.io/eddyblair/pen/VjpNQQ

我所做的是:

  • 将地图和覆盖物包裹在一个容器中, overflow: hidden

  • position: absolute覆盖覆盖position: absolute

  • 通过设置负margin-left将地图的表观宽度扩展到覆盖宽度(加上任何填充和偏移)。

  • 然后为了符合https://www.google.com/permissions/geoguidelines/attr-guide.html定位小部件和归属div s。

这样,地图的中心就与所需区域的中心对齐。 js 只是标准的地图 js。

重新定位街景图标是读者的练习:)


如果您想要左侧的叠加层,只需将第24margin-left更改为margin-right ,将第 32 行right更改为left

经过广泛的搜索,我找不到一种方法来做到这一点,也包括缩放。 谢天谢地,一个聪明的家伙已经想通了。 这里还有一个小提琴

 '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>

可以在此处找到偏移路线或一组标记的另一种方法:

https://stackoverflow.com/a/26192440/1238965

它仍然使用@Andrew Leach 回答中描述的fromLatLngToPoint()方法。

下面是一个SIMPLE使用的例子:

  • 左侧OVERLAY且宽度为500桌面
  • 当页面底部OVERLAY且高度为250时, MOBILE版本

当屏幕尺寸实时变化时,该解决方案也将顺利运行。

要求查询CSS被定义与@media screen and (min-width: 1025px)@media screen and (max-width: 1024px)

代码:

function initMap() {
    map = new google.maps.Map(document.getElementById("map"), {
        zoom: 13
    });

    bounds = new google.maps.LatLngBounds();
    // add markers to bounds

    // Initial bounds
    defineBounds()

    // Trigger resize browser and set new bounds
    google.maps.event.addDomListener(window, 'resize', function() {
        defineBounds()
    });
}

function defineBounds() {
    if (window.matchMedia("(min-width:1025px)").matches) {
        map.fitBounds(bounds, {
            top: 100,
            right: 100,
            left: 600, // 500 + 100
            bottom: 100
        });
    } else {
        map.fitBounds(bounds, {
            top: 80,
            right: 80,
            left: 80,
            bottom: 330 // 250 + 80
        });
    }
}

该解决方案可以针对位于屏幕两侧的 OVERLAY 进行定制

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM