我是D3js和GeoJSON的新手,想知道如何从GeoJSON文件中渲染多边形。
我的GeoJSON文件来自ESRI Shapefile,而没有prj文件,因此我没有有关投影的信息,但绝对不是WGS 84。
使用第三方工具,我可以将GeoJSON转换为SVG,格式如下: (我有65000条路径。)
<path style="fill=blue;stroke-width:0.035;stroke-linecap:round;stroke-linejoin:round;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 297.378906 316.671875 L 298.539063 316.628906 " transform="matrix(1,0,0,-1,0,842)"/>
当我打开此svg文件时,它看起来像这样,这是正确的:
因此,现在我想直接从GeoJSON文件中使用d3js生成这些路径,但是以某种方式将其弄乱了:
到目前为止,这是我的代码:
<!DOCTYPE html>
<meta charset="utf-8">
<style>
</style>
<body>
<script src="https://d3js.org/d3.v5.min.js" charset="utf-8"></script>
<script src="https://d3js.org/topojson.v1.min.js"></script>
<script>
var width = 960,
height = 1160;
var svgContainer = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.style("border", "2px solid steelblue");
const xhr = new XMLHttpRequest();
xhr.open('GET', 'poly.json');
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.responseType = 'json';
xhr.onload = function () {
if (xhr.status !== 200) return
var projection = d3.geoAlbers()
.scale(1)
.translate([0, 0]);
var path = d3.geoPath()
.projection(projection);
var b = path.bounds(xhr.response),
s = .95 / Math.max((b[1][0] - b[0][0]) / width, (b[1][1] - b[0][1]) / height),
t = [(width - s * (b[1][0] + b[0][0])) / 2, (height - s * (b[1][1] + b[0][1])) / 2];
projection
.scale(s)
.translate(t);
svgContainer.append("rect")
.attr('width', width)
.attr('height', height)
.style('stroke', 'black')
.style('fill', 'orange');
svgContainer.selectAll("path").data(xhr.response.features).enter().append("path")
.attr("d", path)
.style("fill", "red")
.style("stroke-width", "1")
.style("stroke", "blue")
};
xhr.send();
</script>
谁能告诉我如何使用d3js生成类似于第三方工具的路径?
编辑:这是我的GeoJSON数据: https://jsonblob.com/b6664214-4b12-11e9-bbf0-3d8b9aa005c2
答案 0 :(得分:2)
您正在使用投影:d3.geoAlbers()
,该投影采用纬度/经度对并将其投影到笛卡尔网格中。但是,您还要注意,您的数据不使用纬度/经度对(省略了详细信息,因此不使用WGS84)。所有d3.geoProjections都采用球面坐标并将其投影到二维网格,但是您的数据已经是笛卡尔坐标系,因此我们需要一种不同的方法。
相反,请使用d3.geoIdentity()
,它的默认形式与空投影相同:将值视为像素值并进行映射。但是,它也允许我们使用一些标准的d3.geoProjection方法,例如缩放和翻译。
更重要的是,它允许我们访问方法.fitSize(或fitExtent),该方法使我们可以转换和缩放标识变换,从而使要素居中并缩放到地图大小。 fitSize方法采用以像素为单位的宽度和高度,显示要素的区域以及geojson object (不是要素数组)。
var projection = d3.geoIdentity()
.fitSize([width,height],geojsonObject)
现在,许多投影坐标系的左下角都有[0,0],但是SVG的左上角有[0,0],如果需要,我们还可以在y轴上应用翻转:
var projection = d3.geoIdentity()
.reflectY(true)
.fitSize([width,height],geojsonObject)
Here's一个没有y反射的演示(您很可能需要它)
此方法随d3v4 +一起提供,让我们避免使用旧的v3手动对中和缩放对象的方式:
var projection = d3.geoAlbers()
.scale(1)
.translate([0, 0]);
var path = d3.geoPath()
.projection(projection);
var b = path.bounds(xhr.response),
s = .95 / Math.max((b[1][0] - b[0][0]) / width, (b[1][1] - b[0][1]) / height),
t = [(width - s * (b[1][0] + b[0][0])) / 2, (height - s * (b[1][1] + b[0][1])) / 2];
projection
.scale(s)
.translate(t);
但是,如果您希望用功能覆盖其他数据,则需要具有相同投影的数据,或者需要知道数据的投影是什么,以便可以重新投影部分或全部将数据集转换为通用格式。如果我不得不猜测,我会说您的投影是UTM区域32N(基于字段名称)。通常,如果您拥有以百万为单位的北/南值,则您正在使用UTM,因为它代表距赤道的米。如果放置在UTM区域32N中,您可能正在看:
我现在很感兴趣,假设我得到了投影,我猜想墓地是小块,只有1.3mx 3m,而且它们的对齐方式表明不是建筑物,并且它的位置看起来像专用绿色空间和其他用途与周围地区相比会太密集
如果看起来正确,则可以将其用作prj文件(原点可能不正确,这可能会引起一些偏移
:PROJCS["WGS_1984_UTM_Zone_32N",GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",SPHEROID["WGS_1984",6378137.0,298.257223563]],PRIMEM["Greenwich",0.0],UNIT["Degree",0.0174532925199433]],PROJECTION["Transverse_Mercator"],PARAMETER["False_Easting",500000.0],PARAMETER["False_Northing",0.0],PARAMETER["Central_Meridian",9.0],PARAMETER["Scale_Factor",0.9996],PARAMETER["Latitude_Of_Origin",0.0],UNIT["Meter",1.0]]