给出EPSG预测(例如,这个阿拉巴马州的预测:[http://spatialreference.org/ref/epsg/26729/][1])
如何以可以在D3.js投影中使用它们的方式获取给定的WGS84投影边界。
例如,您如何知道用于显示地图的投影,旋转度或边界框?
答案 0 :(得分:16)
这是一个相当复杂的问题。答案将根据您正在查看的空间参考(SRS或坐标参考系统(CRS))系统以及您的最终目标而有所不同。
我在这个答案中使用了d3.js v4
例如,您如何知道投影,旋转度或 用于显示地图的边界框?
没有严格的规则包含所有预测。查看投影参数通常可以为您提供足够的信息来快速创建投影 - 假设投影在d3中开箱即用。
我可以给出的关于设置参数的最佳建议,如何时旋转或何时居中,使用的平行等等,是在精炼投影时缩小方向,以便您可以看到每个参数在做什么以及在哪里正在寻找。然后进行缩放或范围拟合。那,并为你的边界框使用geojson验证器,如this one。
最后,您总是可以使用投影数据并完全删除d3.geoProjection(this question),如果您的所有数据都已投影在同一投影中,则尝试定义投影是一个没有实际意义的点。
我很快就会注意到,如果你看一下基准面之间的差异,问题可能会进一步复杂化。例如,您引用的SRS使用NAD27 datum。基准面是地球形状的数学表示,NAD27将不同于NAD83或WGS84,尽管所有都是以度为单位测量的,因为基准面代表地球的三维表面。如果您正在混合使用冲突基准的数据,您可能会遇到一些精确问题,例如,根据您的需要,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]]
这是对投影的非常标准的描述。每种类型的投影都有特定于它的参数,因此这些参数永远不会相同。
本说明书中最重要的部分是:
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,因为这会旋转地图下方的圆柱投影,给出相同的结果平移)。
如果我们缩小了一点,这就是投影的样子:
它可能看起来很扭曲,但它只是为了显示阿拉巴马州内和附近的区域。放大它开始看起来更正常:
接下来的问题很自然:规模怎么样?那么这将根据您的视口大小和您想要显示的区域而有所不同。并且,您的投影未指定任何边界。如果要显示地图投影的范围,我会在答案末尾触及边界。即使投影具有边界,它们也可能与您想要显示的区域(通常是整个投影边界的子集)对齐。
在其他地方居中怎么样?假设您只想展示一个并非恰好位于投影中心的城镇?好吧,我们可以使用中心。因为我们在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是底部。返回投影。
我将使用问题here中的示例来演示使用边界框来帮助定义投影。目标是使用以下知识来投射map below:它的投影和它的边界框(我把它放在手边,并且找不到一个定义好的边界框的好例子):
然而,在我们到达边界框坐标之前,让我们看一下投影。在这种情况下,它是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"],
现在,边界框由这些限制定义:
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):