Я пытаюсь отобразить карту мира с данными о высоте, используя D3.
Для этого я использую Natural Earth 50m land geojson: https://github.com/martynafford/natural-earth-geojson/tree/master/50m/physical
И растровые данные естественной высоты Земли: https://www.naturalearthdata.com/downloads/50m-raster-data/50m-shaded-relief/
Я использую этот учебник: https://datawanderings.com/2020/08/08/растровые фоны/
Итак, я сначала нашел границы геоджсона:
ogrinfo ne_50m_land.json -so -al | grep Extent
Extent: (-180.000000, -89.998926) - (180.000000, 83.599609)
Затем я вырезал файл TIF:
gdal_translate -projwin -180.000000 83.599609 180.000000 -89.998926 SR_50M.tif SR_50M-box.tif
И спроецируйте в проекцию Меркатора:
gdalwarp -overwrite -s_srs "+proj=longlat +datum=WGS84 +no_defs" -t_srs EPSG:3395 SR_50M-box.tif SR_50M-proj.tif
Наконец, я экспортирую в PNG:
gdal_translate -of PNG SR_50M-proj.tif SR_50M.png
Затем я визуализирую все с помощью D3.
this.projection = d3.geoMercator()
.translate([0, 0])
.scale(1)
this.rasterImage = new Image();
this.rasterImage.src = raster;
this.path = d3.geoPath(this.projection, this.ctx);
this.bb = this.path.bounds(this.land);
const s = 1 / Math.max((this.bb[1][0] - this.bb[0][0]) / this.getWidth(), (this.bb[1][1] - this.bb[0][1]) / this.getHeight());
// transform
const t = [(this.getWidth() - s * (this.bb[1][0] + this.bb[0][0])) / 2, (this.getHeight() - s * (this.bb[1][1] + this.bb[0][1])) / 2];
// update projection
this.projection
.scale(s)
.translate(t);
this.raster_width = (this.bb[1][0] - this.bb[0][0]) * s;
this.raster_height = (this.bb[1][1] - this.bb[0][1]) * s;
this.rtranslate_x = (this.getWidth() - this.raster_width) / 2;
this.rtranslate_y = (this.getHeight() - this.raster_height) / 2;
this.ctx.beginPath();
this.path(this.land);
this.ctx.fill();
this.ctx.save();
this.ctx.globalAlpha = 0.8;
this.ctx.translate(this.rtranslate_x, this.rtranslate_y);
this.ctx.drawImage(this.rasterImage, 0, 0, this.raster_width, this.raster_height);
this.ctx.restore();
Однако в конце земля геоджсона и растр не выровнены:
Я пытался сначала проецировать, а затем вырезать или использовать Псевдо-Меркатор или Меркатор при проецировании, но ничего не работает. У кого-нибудь есть идеи?
Меркатор обычно обрезается примерно на 85 градусах северной/южной широты (~85,05113 северной/южной широты) — чем дальше, тем выше вы получите карту, высота которой больше, чем ширина, и карта становится намного выше на каждый дополнительный градус севера/юга. входит в объем..
Функции клипов D3, использующие это ограничение:
The spherical Mercator projection. Defines a default projection.clipExtent such that the world is projected to a square, clipped to approximately ±85° latitude.
Северные границы в порядке, но южные границы геоджсона — это -89.998926
градусов, которые вы используете для вырезания изображения. Но поскольку D3 обрезает geojson, вы растягиваете изображение на другую величину по сравнению с geojson, отсюда и проблема, которую вы видите.
Решение должно состоять в том, чтобы обрезать изображение до границ, которые представляют пределы того, что D3 будет отображать для Меркатора (85.05113
градусов южной широты), а не пределы самих данных.
Я не смотрел, насколько точно gdal реализует EPSG:3395, поскольку определение предусматривает проецируемые границы 80 градусов на юг и 84 градуса на север - хотя, глядя на изображение, это не кажется проблемой.
Вы также можете использовать более чистые методы fitSize для проекций D3 (d3v4+):
projection.fitSize([width,height],geojsonObject)
Который установит масштаб и переведет для вас заданную ширину/высоту.
Я обрезал изображение TIF, используя -85.05113 в качестве южной границы, и это работает! Есть еще очень незначительный сдвиг, но на данный момент этого достаточно. Спасибо