有没有办法使用 JavaScript 从 tiff 图像中提取地理标签?

mis*_*toe 2 javascript tiff maptiler

我正在开展一个项目,需要使用正射马赛克地图。该地图采用 .tiff 格式。我想从该地图中提取地理信息,以便我可以将其正确覆盖在谷歌地图上。我在这个项目中使用平均堆栈。

所以我的最后一个问题是:有没有办法使用 JavaScript 从 tiff 图像中提取地理标签?

编辑:当我使用 Maptiler(https://www.maptiler.com/)时,它会自动将 .tif 文件正确放置在谷歌地图上。我想知道它如何提取信息并将其放置在地图上,以便我可以自己完成。

Mik*_*ans 5

为了读取 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's pixels and fields
const { data:pixels, fields, width, height } = image[0];

// let's see what tags apply
console.log(fields);
Run Code Online (Sandbox Code Playgroud)

产量(对于这个特定的 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'
}

Run Code Online (Sandbox Code Playgroud)

我想知道它如何提取信息并将其放置在地图上,以便我可以自己完成。

系好安全带,这将变得更加详细。

标签号都是明确定义的东西,可以在https://www.awaresystems.be/imaging/tiff/tifftags.html上找到,其中所有低编号的都是通用 TIFF 标签(256 和 257 是图像的宽度和高度,274 是“该图像是否旋转”,284 告诉我们像素是否存储为 RGBRGBetc 还是作为单独的 R、G 和 B 数组等),数字较大的是“私有”标签,仅使用在特定的背景下。例如,339告诉我们像素使用二进制补码存储有符号整数,33550模型像素比例标签,34737GeoASCII 参数标签等。

其中,34735 可以说是最重要的标签,因为它包含地图软件用来正确放置图像的GeoKey 字典:

第一行 ,1 1 0 5内容如下:

  • “蒂夫版本 1”,
  • “keys revision 1.0”(编码为单独的“句号之前”和“句号之后”整数值),
  • “这本词典中有5 个条目

GeoKey 字典中的每个条目都按“键、tifftaglocation、计数、值”排序,字段为tifftaglocation0(所有“值”字段使用 SHORT,无符号 16 位整数数据类型)或 1(tiff 标记的正式规范告诉您如何解码该值),该count字段告诉我们该值由多少个 SHORT 组成。

在这种情况下:

  • 1024(模型类型)的值为 2:数据表示基于地理纬度-经度系统的映射。
  • 1025(光栅类型)的值为1:每个像素代表一个区域(例如,它是平均高度图,而不是像素是具有精确已知高程的特定点,但仅限于该点)
  • 2048(地理类型)的值为 4326:这是 WGS84 数据,例如它使用(Web)墨卡托投影,几乎所有制图软件都使用
  • 2052(线性单位)的值为 9001:数据使用 EPSG 线性单位(即“十进制度”,因此值 38.25 表示 38 个整数单位加四分之一个单位)
  • 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
  ],
  ...
Run Code Online (Sandbox Code Playgroud)

33922 中的值告诉我们,像素索引 x=0、y=0(具有未使用的 z 值 0)映射到现实世界弧度坐标 x=168、y=-44,即 S44 E168(也具有未使用的 z 值) -值 0) 和 33550 告诉我们图像中的像素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

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'
}

Run Code Online (Sandbox Code Playgroud)

(变换反转在https://gis.stackexchange.com/a/452575/219296上进行了解释)

使用此代码,我们可以在 GPS 坐标和像素坐标之间进行转换。例如,运行以下代码:

  ...
  33550 => Float64Array(3) [
    0.0002777777777777778, 0.0002777777777777778, 0
  ],
  33922 => Float64Array(6) [
      0,   0,  0,
    168, -44,  0
  ],
  ...
Run Code Online (Sandbox Code Playgroud)

针对上述 TIFF 数据生成以下日志:

GPS: (-44.9856891,168.321971), PX: (1159,3548), elevation: 1421m
Run Code Online (Sandbox Code Playgroud)

或者走另一条路:

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);

const [
  _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 };
}
Run Code Online (Sandbox Code Playgroud)

针对上述 TIFF 数据生成以下日志:

PX: (278,1882) maps to GPS: (-44.522777777777776,168.07722222222222)
Run Code Online (Sandbox Code Playgroud)