将EPSG投影边界转换为D3.js

时间:2017-02-15 20:13:51

标签: d3.js gis projection extent

给出EPSG预测(例如,这个阿拉巴马州的预测:[http://spatialreference.org/ref/epsg/26729/][1]

如何以可以在D3.js投影中使用它们的方式获取给定的WGS84投影边界。

例如,您如何知道用于显示地图的投影,旋转度或边界框?

1 个答案:

答案 0 :(得分:16)

这是一个相当复杂的问题。答案将根据您正在查看的空间参考(SRS或坐标参考系统(CRS))系统以及您的最终目标而有所不同。

我在这个答案中使用了d3.js v4

简答:

  

例如,您如何知道投影,旋转度或   用于显示地图的边界框?

没有严格的规则包含所有预测。查看投影参数通常可以为您提供足够的信息来快速创建投影 - 假设投影在d3中开箱即用。

我可以给出的关于设置参数的最佳建议,如何时旋转或何时居中,使用的平行等等,是在精炼投影时缩小方向,以便您可以看到每个参数在做什么以及在哪里正在寻找。然后进行缩放或范围拟合。那,并为你的边界框使用geojson验证器,如this one

最后,您总是可以使用投影数据并完全删除d3.geoProjection(this question),如果您的所有数据都已投影在同一投影中,则尝试定义投影是一个没有实际意义的点。

基准面

我很快就会注意到,如果你看一下基准面之间的差异,问题可能会进一步复杂化。例如,您引用的SRS使用NAD27 datum。基准面是地球形状的数学表示,NAD27将不同于NAD83WGS84,尽管所有都是以度为单位测量的,因为基准面代表地球的三维表面。如果您正在混合使用冲突基准的数据,您可能会遇到一些精确问题,例如,根据您的需要,NAD27和NAD83之间的基准平移并非无关紧要(维基百科截图,无法链接到图像):

enter image description here

如果由于使用多个基准而导致的位置变化是一个问题,那么您需要的不仅仅是d3才能将它们转换为一个标准基准。 D3假设您正在使用WGS84,即GPS系统使用的数据。如果这些转变不是问题,那么忽略这部分答案。

示例投影

所以,让我们看看你的预测,EPSG:26729

PROJCS["NAD27 / Alabama East",
    GEOGCS["NAD27",
        DATUM["North_American_Datum_1927",
            SPHEROID["Clarke 1866",6378206.4,294.9786982138982,
                AUTHORITY["EPSG","7008"]],
            AUTHORITY["EPSG","6267"]],
        PRIMEM["Greenwich",0,
            AUTHORITY["EPSG","8901"]],
        UNIT["degree",0.01745329251994328,
            AUTHORITY["EPSG","9122"]],
        AUTHORITY["EPSG","4267"]],
    UNIT["US survey foot",0.3048006096012192,
        AUTHORITY["EPSG","9003"]],
    PROJECTION["Transverse_Mercator"],
    PARAMETER["latitude_of_origin",30.5],
    PARAMETER["central_meridian",-85.83333333333333],
    PARAMETER["scale_factor",0.99996],
    PARAMETER["false_easting",500000],
    PARAMETER["false_northing",0],
    AUTHORITY["EPSG","26729"],
    AXIS["X",EAST],
    AXIS["Y",NORTH]]

这是对投影的非常标准的描述。每种类型的投影都有特定于它的参数,因此这些参数永远不会相同。

本说明书中最重要的部分是:

NAD27 / Alabama East投影名称,不是必需的,而是一个很好的参考,因为它比EPSG编号更容易记住,而参考/工具只能使用通用名称而不是EPSG编号。

PROJECTION["Transverse_Mercator"]我们正在处理的投影类型。这定义了表示地球表面上的点的3d坐标如何转换为笛卡尔平面上的2d坐标。如果您在此处看到未在受支持的投影(v3 - v4)的d3列表中列出的投影,那么您在定义自定义投影时需要做一些工作。但是,通常情况下,您会找到与此匹配的投影。无论地图是在每个轴上旋转还是居中,投影类型都会发生变化。

PARAMETER["latitude_of_origin",30.5],
PARAMETER["central_meridian",-85.83333333333333],

这两个参数设置投影的中心。对于横向墨卡托,只有中央子午线很重要。 See this demo of the effect of choosing a central meridian on a transverse Mercator

原点的纬度主要用于设置northnigs的参考点。中央子午线也为东方做这个,但如上所述,设置中心子午线,其中失真从极到极最小化(它相当于常规墨卡托上的赤道)。 如果你真的需要有适当的北向和东方,以便你可以比较纸质地图和共享相同投影的网络地图的x,y位置,那么d3可能不是最好的工具。如果您不关心测量笛卡尔坐标空间中的坐标,这些参数无关紧要:D3不会复制投影的坐标系(以英尺为偏东/北方测量),而是复制相同的形状。 SVG坐标空间。

因此,根据投影描述中的相关参数,以投影原点为中心的d3.geoProjection将如下所示:

d3.geoTransverseMercator()
    .rotate([85.8333,0])
    .center([0,30.5])

为什么我大约旋转86度?这就是横向墨卡托的构建方式。在横向墨卡托的demo中,地图沿x轴旋转。以x轴为中心将简单地左右平移地图,而不是改变投影的性质。在demo中很明显,投影正在发生与平移不同的变化,这就是应用的旋转。当我在投影下转动地球时,我使用的旋转是负的。所以这个投影的中心位于-85.833度或西85.8333度。

由于在横轴墨卡托上,沿着子午线的失真是一致的,我们可以向下平移而不需要旋转。这就是我在y轴上使用中心的原因(在这种情况下是,在其他情况下,你也可以在y轴上旋转,使用负y,因为这会旋转地图下方的圆柱投影,给出相同的结果平移)。

如果我们缩小了一点,这就是投影的样子:

enter image description here

它可能看起来很扭曲,但它只是为了显示阿拉巴马州内和附近的区域。放大它开始看起来更正常:

enter image description here

接下来的问题很自然:规模怎么样?那么这将根据您的视口大小和您想要显示的区域而有所不同。并且,您的投影未指定任何边界。如果要显示地图投影的范围,我会在答案末尾触及边界。即使投影具有边界,它们也可能与您想要显示的区域(通常是整个投影边界的子集)对齐。

在其他地方居中怎么样?假设您只想展示一个并非恰好位于投影中心的城镇?好吧,我们可以使用中心。因为我们在x轴上旋转地球,所以任何居中都相对于中央子午线。以[1,30.5]为中心,将地图居中于中央子午线以东1度(西面85.8333度)。所以x分量将相对于旋转,y分量将相对于赤道 - 它的纬度。)

如果坚持投影是很重要的,那么就需要这种奇怪的居中行为,如果没有,可能更容易简单地修改x旋转,以便你有一个看起来像这样的投影:

d3.geoTransverseMercator()
    .center([0,y])
    .rotate([-x,0])
    ...

这将自定义横向墨卡托以针对您的特定区域进行优化,但代价是偏离您的起始预测。

不同的预测类型

不同的投影可能有不同的参数。例如,圆锥投影可以具有一条(切线)或两条(正割)线,这些线表示投影与地球相交的点(因此失真最小化的点)。这些投影(例如Albers或Lambert Conformal)使用类似的方法进行居中(旋转-x,中心y),但有附加参数来指定表示切线或正割线的平行线:

d3.geoAlbers()
    .rotate([-x,0])
    .center([0,y])
    .parallels([a,b])

See this answer on how to rotate/center an Albers(对于目前想到的所有圆锥形投影基本相同)。

平面/方位角投影(我还没有检查过)可能只是居中。但是,每个地图投影可能会有一个略微不同的方法来定位'它(通常是.rotate和.center的组合)。

关于如何设置不同的投影类型/系列,有很多例子和SO问题,这些应该有助于大多数具体的预测。

包装盒

但是,您可能有一个指定边界的投影。或者更可能是image with a bounds and a projection。在这种情况下,您需要指定这些边界。使用.fitExtent的{​​{1}}方法使用geojson功能可以轻松完成此操作:

  

projection.fitExtent(extent,object):

     

设置投影的比例并平移以适合给定范围中心的指定GeoJSON对象。范围指定为数组[[x 0,y 0],[x 1,y 1]],其中x 0是边界框的左边,y 0是顶部,x 1是右边,y 1是底部。返回投影。

(另见this question/answer

我将使用问题here中的示例来演示使用边界框来帮助定义投影。目标是使用以下知识来投射map below:它的投影和它的边界框(我把它放在手边,并且找不到一个定义好的边界框的好例子):

enter image description here

然而,在我们到达边界框坐标之前,让我们看一下投影。在这种情况下,它是like

d3.geoProjection()

由于我们将让d3根据边界框选择比例和中心点,我们只关心几个参数:

PROJCS["ETRS89 / Austria Lambert",
    GEOGCS["ETRS89",
        DATUM["European_Terrestrial_Reference_System_1989",
            SPHEROID["GRS 1980",6378137,298.257222101,
                AUTHORITY["EPSG","7019"]],
            AUTHORITY["EPSG","6258"]],
        PRIMEM["Greenwich",0,
            AUTHORITY["EPSG","8901"]],
        UNIT["degree",0.01745329251994328,
            AUTHORITY["EPSG","9122"]],
        AUTHORITY["EPSG","4258"]],
    UNIT["metre",1,
        AUTHORITY["EPSG","9001"]],
    PROJECTION["Lambert_Conformal_Conic_2SP"],
    PARAMETER["standard_parallel_1",49],
    PARAMETER["standard_parallel_2",46],
    PARAMETER["latitude_of_origin",47.5],
    PARAMETER["central_meridian",13.33333333333333],
    PARAMETER["false_easting",400000],
    PARAMETER["false_northing",400000],
    AUTHORITY["EPSG","3416"],
    AXIS["Y",EAST],
    AXIS["X",NORTH]]

这是两条割线,地图投影拦截地球表面。

PARAMETER["standard_parallel_1",49],
PARAMETER["standard_parallel_2",46],

这是中央子午线,我们将用于沿x轴旋转投影的数字(正如人们想到的所有圆锥投影一样)。

最重要的是:

   PARAMETER["central_meridian",13.33333333333333],

这一行给出了我们的投影族/类型。

这总共给我们提供了类似的东西:

PROJECTION["Lambert_Conformal_Conic_2SP"],

现在,边界框由这些限制定义:

  • 东:17.2度
  • West:9.3度
  • 北:49.2度
  • 南:46.0​​度

d3.geoConicConformal() .rotate([-13.33333,0] .parallels([46,49]) (和.fitExtent)方法采用geojson对象并适当地平移和缩放投影。 我在这里使用.fitSize,因为它会跳过边界周围的边距(.fitSize允许提供边距,这是唯一的区别)。所以我们需要用这些边界创建一个geojson对象:

fitExtent

请记住使用right hand rule,并使你的终点与起点相同(否则无休止的悲伤)。

现在我们所要做的就是调用这个方法,我们将进行预测。 由于我使用图像验证我的投影参数,我知道我想要的宽高比。如果您不知道宽高比,则可能会有一些多余的宽度或高度。这给了我类似的东西:

var bbox = {          
          "type": "Polygon",
          "coordinates": [
            [
              [9.3, 49.2], [17.2, 49.2],  [17.2, 46], [9.3, 46], [9.3,49.2]
            ]
          ]
        }

一个看起来很开心的最终产品(记住一个严重下采样的世界topojson):

enter image description here