如何在Google Maps API V3中偏移中心点


78

我有一张Google地图,其中的半透明面板覆盖了该区域的一部分。我想调整地图的中心点,以考虑部分遮挡的地图部分。参见下图。理想情况下,放置十字线和大头针的位置将是地图的中心点。

我希望这是有道理的。

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

提前致谢!

我正在建立的地图样机


您将不得不澄清您的问题。“考虑部分被遮盖的地图部分”是什么意思?“而不是50%而不是50%”是什么意思?想要提供帮助,但无法弄清楚您要实现的目标。
肖恩·米奇

嗨,您能提供工作示例的网址吗,因为我需要一些帮助来创建一条移动的线路以及一条路线
SP Singh 2013年

Answers:


83

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

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

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

}

是否可以在加载页面时触发此代码,以便在首次查看时地图显示为偏移量?
达伦·库克

@DarrenCook:当然可以。只需使用代替即可map.setCenter()。这些代码要做的就是重新计算要告知地图要执行的操作,然后再执行自己的操作map.setCenter()
安德鲁·里奇

谢谢安德鲁,所以在我的代码中,我使用addMarkerFromAdress(latLng,title)并替换为函数offsetCenter(latlng,offsetx,offsety),它仍然可以正常工作-但如何将offsety(243px)的值输入函数中?
达伦·库克

27
更好地使用map.panBy(250, 0);
Alexander Shvetsov

2
我发现nw该脚本从未使用过var,至少在我的用例中可以删除它而没有任何问题。我没有自己尝试过,因为我有很多其他方法已经设置了界限。
hellatan

69

还要看看maps对象上的panBy(x:number,y:number)函数。

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

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

像这样使用它:

mapsObject.panBy(200, 100)

7
有人知道总是禁用此方法的动画的方法吗?
2013年

这一定是答案。它运行完美,非常容易。谢谢。
Developer107

10

这是一个更简单的方法,在响应式设计中可能会更有用,因为您可以使用百分比而不是像素。没有世界坐标,没有LatLngs to Points!

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

在InfoBox上试过了,就可以了!(尽管pixelOffset仍需要修复)

1
解决方案的简单性以及考虑响应式设计的绝佳答案
Daniel Tonon

1
我收到未捕获的TypeError:无法读取未定义的属性“ toSpan”
Mike Kormendy '16

我们可以长期得到newCenter吗?
Tomar先生,

1
如果需要在同一操作中更改缩放比例,则此操作将无效。另外,如果目的地(新)中心在地理位置上与当前中心相距较远,则无法正常工作。即使在相同的缩放级别,例如,由于使用了墨卡托投影,屏幕宽度的0.25表示在赤道上的距离与在挪威的距离也不同。投影最终坐标更加稳健。
pscl


8

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

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

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

进一步阅读:官方文档


太棒了!
monchisan

6

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

1
这个答案与安德鲁的答案结合(放在后面)是我的解决方案。谢谢纳休尔!
Mike Kormendy

5

老问题,我知道。但是,以CSS为中心的方式又如何呢?

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

我所做的是:

  • 将地图包装并覆盖在容器中 overflow: hidden

  • 用覆盖 position: absolute

  • 通过设置负数,将地图的表观宽度扩展为叠加宽度(加上所有填充和偏移量)margin-left

  • 然后,为了符合https://www.google.com/permissions/geoguidelines/attr-guide.html放置窗口小部件和归因div

这样,地图的中心与所需区域的中心在一条直线上。该js只是标准地图js。

重新定位图标以查看街景是读者的一项练习:)


如果要在左侧显示叠加层,只需将行更改24 margin-leftmargin-right并将第32行更改为right即可left


我真的很喜欢这种方法。保持Angular / javascript整洁。
群星

谢谢:)当然,每当Google更改样式时,都需要更新。我在移动属性和控件等的CSS前面加了注释。如果需要修改,它应该给出我如何做的想法。需要创建其他CSS才能整理街道视图。
Mardoxx

3

经过广泛的搜索,我找不到包含缩放功能的方法。幸运的是,一个聪明的家伙已经弄清楚了。这里也有一个小提琴

'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 using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.