88

エリアの一部を覆う半透明のパネルが付いたGoogleマップがあります。マップの部分的に隠されている部分を考慮して、マップの中心点を調整したいと思います。下の画像を参照してください。理想的には、十字線とピンが配置される場所がマップの中心点になります。

それが理にかなっていることを願っています。

理由は簡単です。ズームするときは、マップを50%50%ではなく十字線の中央に配置する必要があります。また、地図上にマーカーをプロットし、順番に移動していきます。マップがそれらの中心にある場合、それらもオフセット位置にある必要があります。

前もって感謝します!

私が作成している地図のモックアップ

4

10 に答える 10

88

関連する前の答えを見つけたら、これは特に難しいことではありません。

マップの中心をその世界座標に変換し、マップを中心に配置する必要がある場所を見つけて、見かけの中心を目的の場所に配置し、実際の中心を使用してマップを再中心化する必要があります。

map.getCenter()APIは常にマップをビューポートの中心に配置します。そのため、見かけの中心ではなく実際の中心を返すため、使用する場合は注意が必要です。APIをオーバーロードして、APIgetCenter()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);

}
于 2012-05-19T14:28:07.497 に答える
78

また、mapsオブジェクトのpanBy(x:number、y:number)関数も確認してください。

ドキュメントには、関数について次のように記載されています。

マップの中心を指定された距離(ピクセル単位)だけ変更します。距離がマップの幅と高さの両方よりも小さい場合、トランジションはスムーズにアニメーション化されます。マップの座標系は、西から東(x値の場合)および北から南(y値の場合)に増加することに注意してください。

次のように使用してください。

mapsObject.panBy(200, 100)
于 2013-07-09T07:37:46.993 に答える
12

panBy()マップAPIのメソッドを使用して問題を解決する例を次に示します。http: //jsfiddle.net/upsidown/2wej9smf/

于 2014-12-08T12:11:02.160 に答える
11

別の最も簡単な解決策を見つけました。fitBoundsメソッドを使用している場合は、オプションの2番目の引数をに渡すことができます。この引数はパディングであり、境界をフィッティングするときに考慮されます。

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

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

さらに読む:公式ドキュメント

于 2018-06-26T11:25:58.963 に答える
10

ピクセルの代わりにパーセンテージを使用できるため、レスポンシブデザインで役立つ可能性のある簡単な方法を次に示します。世界座標も、ポイントへのLatLngもありません!

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);
于 2015-04-30T01:36:43.847 に答える
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; 
  }
});
于 2013-05-22T20:38:07.813 に答える
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-leftをにmargin-right、行32rightをに変更しleftます。

于 2016-06-29T15:11:43.850 に答える
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>

于 2016-08-23T11:23:33.897 に答える
1

ルートまたはマーカーのグループをオフセットする場合の別のアプローチは、次の場所にあります。

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

fromLatLngToPoint()@AndrewLeachの回答で説明されている方法を引き続き使用します。

于 2014-10-05T08:52:58.363 に答える
0

以下は、SIMPLEの使用例です。

  • 左側OVERLAYで幅がOVERLAYの場合のDESKTOPバージョンの場合500
  • ページ下部OVERLAYで、高さが250

このソリューションは、画面サイズがライブで変更されたときにもスムーズに機能します。

クエリを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用にカスタマイズできます

于 2021-04-08T10:45:11.337 に答える