为了读取 TIFF 标签,您需要一个适当的 TIFF 解析器,例如tiff包,以便您可以运行非位图部分IFD 块:
import tiff from "tiff";
// Load a GeoTIFF file
const file = fs.readFileSync(`ALPSMLC30_S045E168_DSM.tif`);
// We can do this in the browser, too, the `decode` function takes an ArrayBuffer
const image = tiff.decode(file.buffer);
// get the first IFD block
const block = image[0];
const pixels = block.data;
// let's see what tags apply
const fields = block.fields;
console.log(fields);
产量(对于这个特定的 GeoTIFF):
Map(19) {
254 => 0,
256 => 3600,
257 => 3600,
258 => 16,
259 => 1,
262 => 1,
273 => Uint32Array(3600) [
... 3600 more items
],
274 => 1,
277 => 1,
278 => 1,
279 => Uint32Array(3600) [
... 3600 more items
],
282 => 1,
283 => 1,
284 => 1,
339 => 2,
33550 => Float64Array(3) [
0.0002777777777777778,
0.0002777777777777778,
0
],
33922 => Float64Array(6) [
0,
0,
0,
168,
-44,
0
],
34735 => Uint16Array(24) [
1, 1, 0, 5,
1024, 0, 1, 2,
1025, 0, 1, 1,
2048, 0, 1, 4326,
2052, 0, 1, 9001,
2054, 0, 1, 9102
],
34737 => 'WGS-84'
}
我想知道它如何提取信息并将其放置在地图上,以便我可以自己完成。
系好安全带,这将变得更加详细。
标签号都是明确定义的东西,可以在上面列出https://www.awaresystems.be/imaging/tiff/tifftags.html,例如33550
is the 模型像素比例 tag, 34737
is the GeoASCII 参数标签等
其中,34735 可以说是最重要的标签,因为它包含GeoKey字典地图软件使用它来正确放置图像:
第一行,1 1 0 5
, reads:
- “蒂夫版本 1”,
- “keys revision 1.0”(编码为单独的主要值和次要值),
- “有5 项在这本词典里”
其中每个条目都按“key、tifftaglocation、count、value(s)”排序,其中tifftaglocation
字段可以是 0(“值”是 SHORT)或 1(tiff 标签的正式规范告诉您如何解码该值),其中count
字段告诉我们该值由多少个 SHORT 组成。
在这种情况下:
- 1024 (型号类型) 的值为 2:地理纬度-经度系统。
- 1025 (光栅类型) 的值为 1:每个像素代表一个区域(例如,它是平均高度图,而不是像素是仅在该点具有确切已知海拔的特定点)
- 2048 (地理类型) 的值为 4326:这是 WGS84 数据,例如它使用几乎所有地图软件都使用的 Web Mercator 投影
- 2052 (线性单位) 的值为 9001:数据使用 EPSG 线性单位(即“十进制度”,因此值 38.25 表示数字通常的含义)。
- 2054 (角度单位) 的值为 9102:数据的角度方面是弧度,因此与之前的值结合我们知道,如果我们看到 38.25,则意味着 38.25 * 1 弧度。
为了进行与地图相关的事情(在没有可以自动为您放置 tiff 的工具的情况下),我们查看标签 33500(它为我们提供地图比例)和 33922(它为我们提供地图翻译):
...
33550 => Float64Array(3) [
0.0002777777777777778,
0.0002777777777777778,
0
],
33922 => Float64Array(6) [
0,
0,
0,
168,
-44,
0
],
...
这告诉我们像素索引 x=0, y=0(未使用的 z 值 0)映射到现实世界弧度坐标 x=168, y=-44,即 S4E E168(也未使用的 z 值 0) )并且图像中的每个像素代表1 degree of arc * 0.00027[...] = 1/3600 arc degree = 1 arc second = 30.87 meters
在 x 和 y 方向上,这使我们能够以适当的平移和比例将数据叠加在地图上。
它还允许我们计算“给定 TIFF 像素的 GPS 坐标”和“给定 GPS 坐标的 TIFF 像素”,基于https://gdal.org/tutorials/geotransforms_tut.html:
function transform(matrix, x, y) {
return [
matrix[0] + matrix[1] * x + matrix[2] * y,
matrix[3] + matrix[4] * x + matrix[5] * y,
];
}
let [sx, sy, _sz] = fields.get(33550);
let [_px, _py, _k, gx, gy, _gz] = fields.get(33922);
// Just like SVG or <canvas>, GeoTIFF uses a "flipped" y coordinate.
sy = -sy;
// Our "forward transform" goes from pixels to geographic coordinate,
// and is the (partial) matrix from the link above.
const pixel_to_geo = [ gx, sx, 0, gy, 0, sy];
// Our "reverse transform" is literally the inverse matrix operation,
// converting geographic coordinates to pixels.
const geo_to_pixel = [-gx/sx, 1/sx, 0, -gy/sy, 0, 1/sy];
function pixelToGeo(x, y) {
const [lat, long] = transform(pixel_to_geo, x, y);
return { lat, long };
}
function geoToPixel(long, lat) {
const [x, y] = transform(geo_to_pixel, long, lat);
return { x: x|0, y: y|0 };
}
(变换反转已在上解释过https://gis.stackexchange.com/a/452575/219296)
使用此代码,我们可以在 GPS 坐标和像素坐标之间进行相互转换:
const lat = 168.321971;
const long = -44.9856891;
const { x, y } = geoToPixel(long, lat);
const elevation = pixels[x + y * block.width];
console.log(`GPS: ${lat},${long}, PX: ${x},${y}, elevation: ${elevation}m`);
根据上述 TIFF 数据,生成以下信息:
GPS: -44.9856891,168.321971, PX: 1159,3548, elevation: 1421m
或者走另一条路:
const [ x, y ] = [
(Math.random() * block.width)|0,
(Math.random() * block.height)|0
];
const { lat, long } = pixelToGeo(x, y);
console.log(`PX: ${x},${y} maps to GPS: ${lat},${long}`);
根据上述 TIFF 数据,生成以下信息:
PX: 278,1882 maps to GPS: -44.522777777777776,168.07722222222222