Comment décaler le point central de L'api GoogleMaps v3

j'ai une carte Google avec un panneau semi transparent couvrant une partie de la zone. Je voudrais ajuster le point central de la carte pour prendre en compte la partie de la carte qui est partiellement occulté. Voir l'image ci-dessous. Idéalement, lorsque le réticule et les broches sont placées au centre de la carte.

j'espère que c'est logique.

la raison est simple: lorsque vous zoomez il a besoin de centrer la carte au-dessus du fauteuil plutôt qu'à 50% de 50%. En outre, je vais tracer des marqueurs sur la carte et se déplacer à travers eux dans l'ordre. Lorsque la carte est centrée sur eux, ils doivent également être à la position offset.

Merci d'avance!

Mockup of the map I am building

69
demandé sur will 2012-05-18 20:55:12

9 réponses

ce n'est pas particulièrement difficile lorsque vous trouvez la réponse précédente pertinente .

vous devez convertir le centre de la carte à ses coordonnées mondiales, trouver où la carte doit être centrée pour mettre le apparent centre où vous le voulez, et re-centre de la carte en utilisant le réel centre.

L'API centrera toujours la carte sur le centre du viewport, donc vous devez être prudent si vous utilisez map.getCenter() car il retournera le Centre réel, pas le centre apparent. Je suppose qu'il serait possible de surcharger L'API pour que ses méthodes getCenter() et setCenter() soient remplacées, mais je ne l'ai pas fait.

Code ci-dessous. exemple en ligne . Dans l'exemple, en cliquant sur le bouton, vous déplacez le centre de la carte (il y a une bifurcation) vers 100px et 200px gauche.

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

}
72
répondu Andrew Leach 2017-05-23 12:18:14

regardez aussi la fonction panBy(X:number, y:number) sur l'objet maps.

la documentation mentionne ceci à propos de la fonction:

change le centre de la carte par la distance en pixels. Si la distance est inférieure à la largeur et la hauteur de la carte, la transition sera très bien animés. Notez que le système de coordonnées augmente d'ouest en est (pour les valeurs de x) et du nord au sud (pour y valeur.)

il suffit de l'utiliser comme ceci:

mapsObject.panBy(200, 100)
60
répondu Kah Tang 2013-07-09 07:37:46

Voici une méthode plus simple qui pourrait être plus utile dans le design responsive puisque vous pouvez utiliser des pourcentages au lieu de pixels. Pas de coordonnées mondiales, pas de LatLngs aux 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);
10
répondu brycewjohnson 2015-04-30 01:36:43

voici un exemple de résolution du problème en utilisant la méthode panBy() de l'API maps: http://jsfiddle.net/upsidown/2wej9smf /

8
répondu Konstantin Kalbazov 2014-12-08 12:11:02

celui D'Andrew est la réponse. Cependant, dans mon cas, la carte.getBounds () n'arrêtait pas de revenir indéfini. Je l'ai fixé en attendant l'événement bounds_changed et puis appellent la fonction pour décaler le centre. Comme ceci:

var center_moved = false;
google.maps.event.addListener(map, 'bounds_changed', function() {
  if(!center_moved){
    offsetCenter(map.getCenter(), 250, -200);
    center_moved = true; 
  }
});
6
répondu Nahuel 2013-05-22 20:38:07

vieille question, je sais. Mais que diriez-vous d'une façon plus CSS-centric?

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

ce que j'ai fait était:

  • envelopper la carte et superposer dans un conteneur avec overflow: hidden

  • Superposé la superposition avec position: absolute

  • largeur apparente par la largeur de recouvrement (plus tout rembourrage et offset) en positionnant un négatif margin-left .

  • ensuite pour se conformer à https://www.google.com/permissions/geoguidelines/attr-guide.html positionne les widgets et l'attribution div s.

ainsi, le centre de la carte est aligné avec le centre de la zone désirée. Le js est juste le JS de la carte standard.

repositionner les icônes pour la vue sur la rue est un exercice pour le lecteur:)


si vous voulez la superposition à gauche, changez simplement la ligne 24 margin-left en margin-right et la ligne 32 right en left .

3
répondu Mardoxx 2016-06-29 15:50:19

après des recherches approfondies Je n'ai pas pu trouver un moyen de faire cela qui comprenait également zoom. Heureusement qu'un " clever chap " l'a découvert. Il y a aussi un violon ici

'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>
3
répondu Chris 2016-08-23 11:23:33

vient de trouver une autre solution. Dans le cas où vous utilisez la méthode fitBounds, vous pouvez passer le deuxième argument optionnel à its. Cet argument est padding, qui sera considéré lors de l'ajustement des limites.

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

// OR use Number:
map.fitBounds(bounds, 20)
"151910920 la" Poursuite de la lecture: officiel docs .

2
répondu Nickensoul 2018-06-26 11:25:58

une autre approche lorsqu'il s'agit de compenser une route ou un groupe de repères peut être trouvée ici:

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

il utilise toujours la méthode fromLatLngToPoint() décrite dans la réponse de @Andrew Leach.

1
répondu MrUpsidown 2017-05-23 12:10:28