d3-geo基本使用

2,164 阅读2分钟

投影

投影的作用是将地球上的一个点(经纬度表示)映射到平面上的一个点. 我以墨卡托举例, 其他投影使用方式类似.

const aProjection = d3.geoMercator();
const latlong = [-122.431, 37.773]; // 经纬度坐标
const [x, y] = aProjection(latlong);

还可以结合geoPath来使用, geoPath用来创建一个地理路径生成器, 路径生成器接收GeoJSON格式的feature对象或geometry对象, 来生成svg path.

geoPath

const aProjection = d3.geoMercator();

// 指定投影
const path = d3.geoPath(aProjection);
// path.projection(aProjection); // 还可以这么指定projection

// 这是一个GeoJSON geometry对象
const point = {
  type: 'Point',
  coordinates: [61.210817, 35.650072],
};


// 注意, 这里虽然是一个点, 但是这个点也使用path来表示的
// 返回svg path命令字符串
path(point); // M335.4664235588574,196.6605620690608m0,4.5a4.5,4.5 0 1,1 0,-9a4.5,4.5 0 1,1 0,9z

// 在svg中生成path
d3.select('svg')
  .append('path')
  .datum(point)
  .attr('d', path);

下面我来看一下GeoJson格式示例:

geoJson = {
  type: "FeatureCollection",
  features: [
    {
      type: "Feature",
      properties: {},
      // Point: 点
      geometry: {
        type: "Point",
        coordinates: [105.38, 31.57],
      },
    },
    {
      type: "Feature",
      properties: {},
      // MultiPoint: 多个点
      geometry: {
        type: "MultiPoint",
        coordinates: [
          [105.38, 31.57],
          [100.38, 30.57]
        ],
      },
    },
    {
      type: "Feature",
      properties: {},
      // LineString: 线, 就是指线段.
      // 记录的是线的端点坐标,可视化时会按照记录顺序联结。对于曲线(如贝塞尔曲线)目前还没有很好的表达,但是在地理数据中,曲线一般会用LineString去拟合,现实地理世界中也没有标准的曲线地理要素。
      geometry: {
        type: "LineString",
        coordinates: [
          [105.38, 31.57],
          [100.38, 30.57]
        ],
      },
    },
    {
      type: "Feature",
      properties: {},
      // MultiLineString: 多条线段
      geometry: {
        type: "MultiLineString",
        coordinates: [
          [
            [105.38, 31.57],
            [100.38, 30.57],
          ],
          [
            [105.38, 31.57],
            [100.38, 30.57],
          ]
        ],
      },
    },
    {
      type: "Feature",
      properties: {},
      // Polygon: 多边形
      geometry: {
        type: "Polygon",
        coordinates: [
          [
            [106.10595703125, 33.33970700424026],
            [106.32568359375, 32.41706632846282],
            [108.03955078125, 32.2313896627376],
            [108.25927734375, 33.15594830078649],
            [106.10595703125, 33.33970700424026]
          ]
        ],
      },
    },
    {
      type: "Feature",
      properties: {},
      // MultiPolygon: 多个多边形
      geometry: {
        type: "MultiPolygon",
        coordinates: [
          // 第一个多边形
          [
            [
              [106.10595703125, 33.33970700424026],
              [106.32568359375, 32.41706632846282],
              [108.03955078125, 32.2313896627376],
              [108.25927734375, 33.15594830078649],
              [106.10595703125, 33.33970700424026]
            ]
          ],
          // 第二个多边形
          [...],
        ],
        
      },
    },
  ],
}

path能接收参数为GeoJSON格式的feature对象或geometry对象:

// 类型为Point、MultiPoint、LineString、MultiLineString、Polygon、MultiPolygon的geometry对象
path({
  type: "Point",
  coordinates: [105.38, 31.57],
});

path({
  type: "MultiPoint",
  coordinates: [
    [105.38, 31.57],
    [100.38, 30.57]
  ],
});

...


// 或者传入feature对象
path({
  type: "Feature",
  properties: {},
  // Point: 点
  geometry: {
    type: "Point",
    coordinates: [105.38, 31.57],
  },
});


// 或者传入整个FeatureCollection
path(geoJson);

在canvas上绘制

上面的例子中path生成了svg path命令字符串, 可以设置给svg path元素的d属性, 从而生成路径.

我们还可以在canvas绘制路径. 我们将canvas 2d context传入geoPath, 这样路径生成函数就会使用canvas api来绘制路径:

const canvas = document.querySelector('canvas');
const context = canvas.getContext('2d');

const aProjection = d3.geoMercator();

const path = d3.geoPath();
path.projection(aProjection);
// 指定context
// 如果不指定context, 调用路径生成器返回SVG path 字符串
// 如果指定context, 则调用canvas 2d相关api绘制路径
path.context(context); 

context.fillStyle = '#ccc';
context.strokeStyle = '#000';
context.beginPath();
path(countries);
context.fill();
context.stroke();

path.area(object)

同样的参数object为GeoJSON格式的feature对象或geometry对象. 它返回该对象在平面上的面积, 注意是图形上的面积, 而不是实际的地理面积. 其中Point、MultiPoint、LineString、MultiLineString面积为0.

path.bounds(object)

返回边界[[x₀, y₀], [x₁, y₁]].

path.centroid(object)

返回中心(重心)

path.measure(object)

返回长度. 其中Point、MultiPoint为0.