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

Lea*_*ple 5 gis projection d3.js extent

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

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

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

And*_*eid 17

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

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

简答:

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

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

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

最后,您可以始终使用投影数据并完全删除d3.geoProjection(此问题),如果您的所有数据都已投影在同一投影中,则尝试定义投影是一个有争议的问题.


基准

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

在此输入图像描述

如果由于使用多个基准而导致的位置变化是一个问题,那么您需要的不仅仅是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]]
Run Code Online (Sandbox Code Playgroud)

这是投影的非常标准的描述.每种类型的投影都有特定于它的参数,因此这些参数并不总是相同的.

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

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

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

PARAMETER["latitude_of_origin",30.5],
PARAMETER["central_meridian",-85.83333333333333],
Run Code Online (Sandbox Code Playgroud)

这两个参数设置了投影的中心.对于横向墨卡托,只有中央子午线很重要.请参阅此演示,了解在横向墨卡托上选择中央子午线的效果.

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


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

d3.geoTransverseMercator()
    .rotate([85.8333,0])
    .center([0,30.5])
Run Code Online (Sandbox Code Playgroud)

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

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

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

在此输入图像描述

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

在此输入图像描述

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

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

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

d3.geoTransverseMercator()
    .center([0,y])
    .rotate([-x,0])
    ...
Run Code Online (Sandbox Code Playgroud)

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


不同的预测类型

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

d3.geoAlbers()
    .rotate([-x,0])
    .center([0,y])
    .parallels([a,b])
Run Code Online (Sandbox Code Playgroud)

请参阅此答案,了解如何旋转/居中Albers(对于此刻想到的所有圆锥投影,这基本相同).

平面/方位角投影(我没有检查过)可能只是居中.但是,每个地图投影在"居中"时可能会略有不同(通常是.rotate和.center的组合).

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


边界框

但是,您可能有一个指定边界的投影.或者更可能的是,带有边界和投影图像.在这种情况下,您需要指定这些边界.使用以下.fitExtent方法使用geojson功能可以轻松完成此操作d3.geoProjection():

projection.fitExtent(extent,object):

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

(另见这个问题/答案)

我将在此处的问题中使用示例来演示使用边界框来帮助定义投影.目标是使用以下知识投影下面地图:它的投影和它的边界框(我把它放在手边,找不到一个定义好的边界框的好例子):

在此输入图像描述

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

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]]
Run Code Online (Sandbox Code Playgroud)

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

PARAMETER["standard_parallel_1",49],
PARAMETER["standard_parallel_2",46],
Run Code Online (Sandbox Code Playgroud)

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

   PARAMETER["central_meridian",13.33333333333333],
Run Code Online (Sandbox Code Playgroud)

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

最重要的是:

PROJECTION["Lambert_Conformal_Conic_2SP"],
Run Code Online (Sandbox Code Playgroud)

这条线给了我们投影系列/类型.

总而言之,这给了我们类似的东西:

d3.geoConicConformal()
   .rotate([-13.33333,0]
   .parallels([46,49])
Run Code Online (Sandbox Code Playgroud)

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

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

.fitExtent(和.fitSize)方法采取以GeoJSON对象,并适当地转换和调整的投影. 我将.fitSize在这里使用它,因为它在边界周围跳过边距(fitExtent允许提供边距,这是唯一的区别).所以我们需要用这些边界创建一个geojson对象:

var bbox = {          
          "type": "Polygon",
          "coordinates": [
            [
              [9.3, 49.2], [17.2, 49.2],  [17.2, 46], [9.3, 46], [9.3,49.2]
            ]
          ]
        }
Run Code Online (Sandbox Code Playgroud)

记住使用右手规则,并使你的终点与你的起点相同(否则无休止的悲伤).

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

var projection = d3.geoConicConformal()
.parallels([46,49])
.rotate([-13.333,0])
.fitSize([width,height],bbox)
Run Code Online (Sandbox Code Playgroud)

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

在此输入图像描述


  • 只是一个提示:在完整答案之前输入简短答案*. (2认同)
  • 我知道这违反了StackOverflow政策,但是@AndrewReid,这是我在此网站上看到的最佳答案之一。非常有用和有用。感谢您对细节的关注! (2认同)
  • @aboutaaron,谢谢,我很感激此评论,这意味着很多 (2认同)