首页 > 解决方案 > 如何计算两个坐标的中心和缩放以保持它们可见

问题描述

我正在与您的图书馆合作项目,这就是我得到的

https://codesandbox.io/s/react-simple-maps-switching-topojson-on-zoom-forked-1s4qh?file=/src/index.js

import React, { useState, useRef, useEffect } from "react";
import { Spring, config, animated } from "react-spring";
import ReactDOM from "react-dom";

import {
  ComposableMap,
  Geographies,
  Geography,
  ZoomableGroup,
  Line,
  Point,
  Marker
} from "react-simple-maps";

const geoUrl =
  "https://raw.githubusercontent.com/zcreativelabs/react-simple-maps/master/topojson-maps/world-110m.json";

const MAX_ZOOM = 8;
const MIN_ZOOM = 1;

const calcZoom = (box, paddingPerc, width, height) => {
  const minXY = box[0];
  const maxXY = box[1];

  console.log(box);

  // find size of map area defined
  let zoomWidth = Math.abs(minXY[0] - maxXY[0]);
  let zoomHeight = Math.abs(minXY[1] - maxXY[1]);

  // increase map area to include padding
  zoomWidth = zoomWidth * (1 + paddingPerc / 100);
  zoomHeight = zoomHeight * (1 + paddingPerc / 100);

  // find scale required for area to fill svg
  const maxXscale = width / zoomWidth;
  const maxYscale = height / zoomHeight;
  const zoomScale = Math.min(maxXscale, maxYscale);

  // handle some edge cases
  // limit to max zoom (handles tiny countries)
  // zoomScale = Math.min(zoomScale, MAX_ZOOM);

  // // limit to min zoom (handles large countries and countries that span the date line)
  // zoomScale = Math.max(zoomScale, MIN_ZOOM);

  return zoomScale;
};

const AnimatedZoomableGroup = animated((props) => {
  return (
    <ZoomableGroup center={[props.lon, props.lat]} {...props}>
      {props.children}
    </ZoomableGroup>
  );
});

export const MapWidget = ({ from, to, width, height }) => {
  const [lat, setLat] = useState(0);
  const [lon, setLon] = useState(0);
  const [zoom, setZoom] = useState(1);

  useEffect(() => {
    if (
      from[0] !== null &&
      to[0] !== null &&
      from[1] !== null &&
      to[1] !== null
    ) {
      // [lon, lat]
      console.log(from, to);
      // const xMid = (from[0] + to[0]) / 2;
      const xMid = (to[0] - from[0]) / 2 + from[0];
      const yMid = (to[1] - from[1]) / 2 + from[1];
      // const yMid = (from[1] + to[1]) / 2;

      const minLon = Math.min(from[0], to[0]);
      const maxLon = Math.max(from[0], to[0]);

      const minLat = Math.min(from[1], to[1]);
      const maxLat = Math.max(from[1], to[1]);

      const bbox = [
        [minLon, maxLon],
        [minLat, maxLat]
      ];

      // setCenter(centroid);
      setLat(yMid);
      setLon(xMid);
      setZoom(calcZoom(bbox, 30, width, height));
    }
  }, [from, to]);

  return (
    <div style={{ width, height }}>
      <ComposableMap>
        <Spring
          from={{ animatedZoom: MIN_ZOOM, lat: 0, lon: 0 }}
          to={{ animatedZoom: zoom, lat: lat, lon: lon }}
          config={{ ...config.slow }}
        >
          {({ animatedZoom, lat, lon }) => (
            <AnimatedZoomableGroup
              maxZoom={MAX_ZOOM}
              minZoom={MIN_ZOOM}
              zoom={animatedZoom}
              lat={lat}
              lon={lon}
            >
              <Geographies
                geography={geoUrl}
                fill="#D6D6DA"
                stroke="#FFFFFF"
                strokeWidth={0.5}
              >
                {({ geographies }) =>
                  geographies.map((geo) => (
                    <Geography key={geo.rsmKey} geography={geo} />
                  ))
                }
              </Geographies>

              {from[0] && to[0] && (
                <Line
                  from={from}
                  to={to}
                  stroke="#FF5533"
                  strokeWidth={6 / zoom}
                  strokeLinecap="round"
                />
              )}

              {from && from[0] !== null && from[1] && (
                <Marker coordinates={from}>
                  <circle r={10 / zoom} fill="#F00" />
                </Marker>
              )}
              {to && to[0] !== null && to[1] && (
                <Marker coordinates={to}>
                  <circle r={10 / zoom} fill="#F00" />
                </Marker>
              )}
            </AnimatedZoomableGroup>
          )}
        </Spring>
      </ComposableMap>
    </div>
  );
};

const App = () => {
  const [from, setFrom] = useState([null, null]);
  const [to, setTo] = useState([null, null]);

  return (
    <div style={{ display: "flex" }}>
      <MapWidget from={from} to={to} width={400} height={200} />
      <button
        onClick={() => {
          setFrom([16.909270516076127, 52.41050505]);
          setTo([-54.9347238, -34.9391291]);
        }}
      >
        1
      </button>

      <button
        onClick={() => {
          setFrom([16.909270516076127, 52.41050505]);
          setTo([-5.6842457, 43.4545162]);
        }}
      >
        2
      </button>
    </div>
  );
};
const rootElement = document.getElementById("root");

ReactDOM.render(<App />, rootElement);

如您所见,我正在尝试最大程度地缩放地图以根据坐标显示两个点

有什么问题,当您单击 2 时,缩放工作正常,但在 1 上,地图被放大到很远 - 我试图找出问题所在,因为我的计算仅在某些情况下有效。

正如您在函数 calcZoom 中看到的那样,我正在根据这两个坐标和 bbox 的质心计算缩放。

我基本上是在尝试将此 d3 示例移植到 react-simple-maps https://medium.com/@andybarefoot/making-a-map-using-d3-js-8aa3637304ee

如果有人可以提供帮助,那就太好了。

最好的!

标签: javascriptreactjsd3.jsmapsreact-simple-maps

解决方案


推荐阅读