Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 49 additions & 6 deletions README.ja.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,14 +78,55 @@ console.log(projected); // [[easting, northing], [easting, northing]] または
メモリが尽きた場合を含む)は throw するので、変換できない target と取り違えることはありません。
入力は lon/lat の度数のみを受け付けます。

### 単位

座標は単位と組にして初めて意味を持ちます。`transformLonLatToCandidate` は同じ lon/lat(度)の
点列を変換し、点列とその単位を組にして `{ points, unit }` で返します。`unit` は `LinearUnit`
(`{ name, metresPerUnit }`。1 単位が何 metre か)です。

- CRS のとき: 単位は PROJ の軸情報から取ります。metre の CRS なら
`{ name: "metre", metresPerUnit: 1 }`、フィートの CRS ならその名前と係数(たとえば `EPSG:2227`
なら `{ name: "US survey foot", metresPerUnit: 1200 / 3937 }`)です。座標系が直交(Cartesian。
軸が度の地理座標系は対象外)で、軸が east / north の 2 次元で、両軸の単位が同じ線形単位の CRS
だけを変換し、それ以外の target は `null` です。
- custom / user の定義の pipeline(同梱の UTJCP 歴史的市区町村の格子など)のとき: 定義は
「格子 → lon/lat(度)」の向きなので、点列は行の格子の単位(定義の `unit`)で返ります。同梱の
3 つの zone は `{ name: "yard (Japan City Plans)", metresPerUnit: 0.9143985307444408 }` を
持っています。`unit` の無い pipeline の行、`unit` が線形単位として不正な行(`name` が空、
`metresPerUnit` が正の有限の number でない)は変換できず `null` になるので、pipeline を持つ
user の定義ファイルには行ごとに使える `unit` を書いてください。
- `transformLonLatArrayToProjected` の契約は変わりません。同じ変換を使い、単位が metre のときだけ
点列を返し、それ以外は `null` を返します。
- Atlas は単位換算をしません。点列は候補の単位のままで、換算や書き戻しは呼ぶ側の仕事です
(この package の transformer を使うときは Teiresias が行います)。

```ts
import { listProjectedCrsFromProjWasm, transformLonLatToCandidate } from "@c4h/atlas";

const rows = await listProjectedCrsFromProjWasm({
extent: { west: 135.6, south: 34.9, east: 135.9, north: 35.1 },
area: "Japan",
includeCustomProjections: true,
onshoreOnly: false,
});
const zoneB = rows.find((row) => row.id === "UTJCP:ZONE-B");

const converted = await transformLonLatToCandidate(zoneB!, [[135.75, 35.0]]);

console.log(converted?.unit); // { name: "yard (Japan City Plans)", metresPerUnit: 0.9143985307444408 }
console.log(converted?.points); // その単位の [[x, y]]。変換できなければ null
```

### Teiresias と組み合わせる

`createCandidateTransformer(rows)` は候補の行から、[`@c4h/teiresias`](https://github.com/code4history/Teiresias)
の `CandidateTransformer` と同じ形の関数を作ります。Atlas で引いた候補を、そのまま `estimateCrs`
で順位づけできます。Atlas は Teiresias に依存しません(型は形で合わせています)。
`geoBasis` が `{ kind: "lonlat_deg", axisOrder: "lon-lat" }` でないとき、および行に無い id のときは
`null` を返し、それ以外は `transformLonLatArrayToProjected` の結果を返します(throw もそのまま
伝えます)。行は関数を作った時点で写し取ります。
`null` を返します。それ以外は、metre の候補なら plain な点列(`transformLonLatArrayToProjected` と
同じ点列)、別の単位の候補なら `{ points, unit }` を返します(throw もそのまま伝えます)。
Teiresias はこの結果の単位を使うので、歴史的市区町村の格子の点列も yard のまま順位づけと
書き出しができます。行は関数を作った時点で `unit` ごと写し取ります。

```ts
import { createCandidateTransformer, listProjectedCrsFromProjWasm } from "@c4h/atlas";
Expand Down Expand Up @@ -163,7 +204,8 @@ proj-wasm 0.1.0-alpha8 は PROJ object を解放してもメモリを全部は
|---|---|
| `listProjectedCrsFromProjWasm(options)` | 適用範囲が `options.extent` を含む投影 CRS の候補を列挙します(基本的な使い方を参照)。戻り値は `ProjectedCrsInfo[]` です。 |
| `initializeProjWasmRuntime(options?)` | 同梱の proj-wasm を最初の検索より前に読み込みます。省略してよく、ほかの呼び出しが必要なときに読み込みます。 |
| `transformLonLatArrayToProjected(target, points, options?)` | 経緯度(度・lon-lat 順)を 1 つの候補の `[easting, northing]` へ変換します。 |
| `transformLonLatArrayToProjected(target, points, options?)` | 経緯度(度・lon-lat 順)を 1 つの候補の `[easting, northing]` へ変換します(metre のときだけ点列を返します)。 |
| `transformLonLatToCandidate(target, points, options?)` | 経緯度(度・lon-lat 順)を 1 つの候補の座標へ変換し、線形単位といっしょに `{ points, unit }` で返します(単位を参照)。 |
| `createCandidateTransformer(rows, options?)` | Teiresias の `estimateCrs` に渡す `AtlasCandidateTransformer` を作ります(Teiresias と組み合わせるを参照)。 |
| `isProjectedPrjPath(pathname)` | 同梱の日本の WKT 集の `.prj` のパスが投影 CRS かどうかを返します。 |
| `inferDatumFamily(pathname)` | そのパスから測地系の系統(`CandidateDatumFamily`)を読みます。 |
Expand All @@ -180,12 +222,13 @@ proj-wasm 0.1.0-alpha8 は PROJ object を解放してもメモリを全部は

| 名前 | 表すもの |
|---|---|
| `ProjectedCrsInfo` | 候補 1 件。`id`・`authority`・`code`・`name`・`areaName`・`deprecated`・`bbox`・`wkt2`・`proj4` と任意の詳細。 |
| `ProjectedCrsInfo` | 候補 1 件。`id`・`authority`・`code`・`name`・`areaName`・`deprecated`・`bbox`・`wkt2`・`proj4` と任意の詳細。custom / user の pipeline の行は格子の `unit` も持ちます。 |
| `LinearUnit` | 座標の線形単位 `{ name, metresPerUnit }`(1 単位が何 metre か)。 |
| `ProjectedCrsExclusion` | 検索から外した行と理由(`onExcluded` に渡ります)。 |
| `ListProjectedCrsFromProjWasmOptions` | `listProjectedCrsFromProjWasm` の option。 |
| `InitializeProjWasmRuntimeOptions` | `initializeProjWasmRuntime` の option(`projWasmAssetBaseUrl`)。 |
| `TransformLonLatToProjectedOptions` | `transformLonLatArrayToProjected` と `createCandidateTransformer` の option。 |
| `AtlasCandidateTransformer` | `createCandidateTransformer` が返す変換関数。 |
| `TransformLonLatToProjectedOptions` | `transformLonLatArrayToProjected`・`transformLonLatToCandidate`・`createCandidateTransformer` の option。 |
| `AtlasCandidateTransformer` | `createCandidateTransformer` が返す変換関数。metre の候補なら plain な点列、別の単位の候補なら `{ points, unit }`、変換できなければ `null` を返します。 |
| `AtlasGeoBasisLike` | 変換関数が受け取る座標の基準(Teiresias の `GeoBasis` と互換)。 |
| `CandidateCrsExtent` | 経緯度の範囲 `{ west, south, east, north }`。 |
| `CandidateBbox` | 経緯度の外接矩形 `{ west, south, east, north }`。 |
Expand Down
60 changes: 53 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,15 +81,59 @@ call fails, for example because the wasm memory is full), the function throws, s
never mistaken for an untransformable target.
The function accepts lon/lat degree input only.

### Units

Coordinates are only meaningful together with their unit. `transformLonLatToCandidate` converts the
same lon/lat degrees and returns the points with the linear unit they are in: `{ points, unit }`,
where `unit` is a `LinearUnit` (`{ name, metresPerUnit }`, the number of metres in one unit).

- For a CRS, the unit comes from the axis information of PROJ. A CRS in metres gives
`{ name: "metre", metresPerUnit: 1 }`; a CRS in feet gives its own name and factor, for example
`{ name: "US survey foot", metresPerUnit: 1200 / 3937 }` for `EPSG:2227`. Only a CRS whose
coordinate system is Cartesian (a projected CRS, not a geographic CRS whose axes are in degrees),
whose axes are east/north in two dimensions, and whose two axes have the same unit, is
transformed; any other target gives `null`.
- For a pipeline of a custom or user definition (the bundled UTJCP historical zones, for example),
the definition describes the grid-to-lon/lat direction, so the points come back in the grid unit
of the row: the `unit` of the definition. The three historical zones declare
`{ name: "yard (Japan City Plans)", metresPerUnit: 0.9143985307444408 }`. A pipeline row without
`unit`, or with a `unit` that is not a linear unit (an empty `name`, a `metresPerUnit` that is not
a positive finite number), cannot be transformed and gives `null`, so a user projection file that
holds pipelines has to give each of them a usable `unit`.
- `transformLonLatArrayToProjected` keeps its contract: it uses the same conversion and returns the
points only when the unit is metre, and `null` otherwise.
- Units are never converted by Atlas. The points stay in the unit of the candidate; converting them,
or writing them out again, is up to the caller (Teiresias does it for the transformer built here).

```ts
import { listProjectedCrsFromProjWasm, transformLonLatToCandidate } from "@c4h/atlas";

const rows = await listProjectedCrsFromProjWasm({
extent: { west: 135.6, south: 34.9, east: 135.9, north: 35.1 },
area: "Japan",
includeCustomProjections: true,
onshoreOnly: false,
});
const zoneB = rows.find((row) => row.id === "UTJCP:ZONE-B");

const converted = await transformLonLatToCandidate(zoneB!, [[135.75, 35.0]]);

console.log(converted?.unit); // { name: "yard (Japan City Plans)", metresPerUnit: 0.9143985307444408 }
console.log(converted?.points); // [[x, y]] in that unit, or null
```

### Using Atlas with Teiresias

`createCandidateTransformer(rows)` turns candidate rows into a transformer with the same shape as
the `CandidateTransformer` of [`@c4h/teiresias`](https://github.com/code4history/Teiresias), so the
candidates found by Atlas can be ranked by `estimateCrs`. Atlas does not depend on Teiresias; the
types match by shape. The transformer returns `null` for a geo basis other than
`{ kind: "lonlat_deg", axisOrder: "lon-lat" }` and for an id that is not among the rows; otherwise it
returns what `transformLonLatArrayToProjected` returns, and throws what it throws. The rows are copied
when the transformer is created.
`{ kind: "lonlat_deg", axisOrder: "lon-lat" }` and for an id that is not among the rows. Otherwise it
returns a plain point list for a candidate in metres (the same points as
`transformLonLatArrayToProjected`) and `{ points, unit }` for a candidate in another unit, and throws
what the conversion throws. Teiresias takes the unit of that result, so the points of a historical
grid are ranked and written out in yards. The rows are copied, `unit` included, when the transformer
is created.

```ts
import { createCandidateTransformer, listProjectedCrsFromProjWasm } from "@c4h/atlas";
Expand Down Expand Up @@ -172,7 +216,8 @@ Everything below is exported from `@c4h/atlas`.
|---|---|
| `listProjectedCrsFromProjWasm(options)` | Lists projected CRS candidates whose area of use contains `options.extent` (see Basic Usage). Returns `ProjectedCrsInfo[]`. |
| `initializeProjWasmRuntime(options?)` | Loads the bundled proj-wasm runtime ahead of the first search. Optional; the other calls load it on demand. |
| `transformLonLatArrayToProjected(target, points, options?)` | Transforms lon/lat degrees (lon-lat order) into `[easting, northing]` of one candidate. |
| `transformLonLatArrayToProjected(target, points, options?)` | Transforms lon/lat degrees (lon-lat order) into `[easting, northing]` of one candidate, in metres only. |
| `transformLonLatToCandidate(target, points, options?)` | Transforms lon/lat degrees (lon-lat order) into the coordinates of one candidate and returns them with their linear unit (`{ points, unit }`; see Units). |
| `createCandidateTransformer(rows, options?)` | Builds an `AtlasCandidateTransformer` for Teiresias' `estimateCrs` (see Using Atlas with Teiresias). |
| `isProjectedPrjPath(pathname)` | Tells whether a `.prj` path of the bundled Japanese WKT collection is a projected CRS. |
| `inferDatumFamily(pathname)` | Reads the datum family (`CandidateDatumFamily`) from such a path. |
Expand All @@ -189,12 +234,13 @@ Everything below is exported from `@c4h/atlas`.

| Name | What it describes |
|---|---|
| `ProjectedCrsInfo` | One candidate: `id`, `authority`, `code`, `name`, `areaName`, `deprecated`, `bbox`, `wkt2`, `proj4` and optional details. |
| `ProjectedCrsInfo` | One candidate: `id`, `authority`, `code`, `name`, `areaName`, `deprecated`, `bbox`, `wkt2`, `proj4` and optional details. A custom or user pipeline row also carries the `unit` of its grid. |
| `LinearUnit` | A linear unit of coordinates: `{ name, metresPerUnit }`, the number of metres in one unit. |
| `ProjectedCrsExclusion` | A row left out of a search, with the reason (given to `onExcluded`). |
| `ListProjectedCrsFromProjWasmOptions` | Options of `listProjectedCrsFromProjWasm`. |
| `InitializeProjWasmRuntimeOptions` | Options of `initializeProjWasmRuntime` (`projWasmAssetBaseUrl`). |
| `TransformLonLatToProjectedOptions` | Options of `transformLonLatArrayToProjected` and `createCandidateTransformer`. |
| `AtlasCandidateTransformer` | The transformer `createCandidateTransformer` returns. |
| `TransformLonLatToProjectedOptions` | Options of `transformLonLatArrayToProjected`, `transformLonLatToCandidate` and `createCandidateTransformer`. |
| `AtlasCandidateTransformer` | The transformer `createCandidateTransformer` returns: a plain point list for a candidate in metres, otherwise `{ points, unit }`, or `null`. |
| `AtlasGeoBasisLike` | The geo basis a transformer receives (compatible with Teiresias' `GeoBasis`). |
| `CandidateCrsExtent` | A lon/lat extent `{ west, south, east, north }`. |
| `CandidateBbox` | A lon/lat bounding box `{ west, south, east, north }`. |
Expand Down
12 changes: 12 additions & 0 deletions data/crs/custom-projections.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@
"north": 46
},
"pipeline": "+proj=pipeline +step +inv +proj=poly +lat_0=40.5 +lon_0=143 +x_0=914398.5307444408 +y_0=1828797.0614888816 +ellps=clrk66 +to_meter=0.9143985307444408 +step +proj=cart +ellps=bessel +step +proj=helmert +x=-146.336 +y=506.832 +z=680.254 +step +inv +proj=cart +ellps=WGS84 +step +proj=unitconvert +xy_in=rad +xy_out=deg",
"unit": {
"name": "yard (Japan City Plans)",
"metresPerUnit": 0.9143985307444408
},
"wkt2": null,
"isApproximate": true,
"source": [
Expand All @@ -33,6 +37,10 @@
"north": 46
},
"pipeline": "+proj=pipeline +step +inv +proj=poly +lat_0=40.5 +lon_0=135 +x_0=914398.5307444408 +y_0=1828797.0614888816 +ellps=clrk66 +to_meter=0.9143985307444408 +step +proj=cart +ellps=bessel +step +proj=helmert +x=-146.336 +y=506.832 +z=680.254 +step +inv +proj=cart +ellps=WGS84 +step +proj=unitconvert +xy_in=rad +xy_out=deg",
"unit": {
"name": "yard (Japan City Plans)",
"metresPerUnit": 0.9143985307444408
},
"wkt2": null,
"isApproximate": true,
"source": [
Expand All @@ -53,6 +61,10 @@
"north": 46
},
"pipeline": "+proj=pipeline +step +inv +proj=poly +lat_0=40.5 +lon_0=127 +x_0=914398.5307444408 +y_0=1828797.0614888816 +ellps=clrk66 +to_meter=0.9143985307444408 +step +proj=cart +ellps=bessel +step +proj=helmert +x=-146.336 +y=506.832 +z=680.254 +step +inv +proj=cart +ellps=WGS84 +step +proj=unitconvert +xy_in=rad +xy_out=deg",
"unit": {
"name": "yard (Japan City Plans)",
"metresPerUnit": 0.9143985307444408
},
"wkt2": null,
"isApproximate": true,
"source": [
Expand Down
Loading
Loading