簡體   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