Skip to content

Commit db7c54c

Browse files
committed
init
0 parents  commit db7c54c

18 files changed

+938
-0
lines changed

Diff for: .github/workflows/ci.yml

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
name: CI
2+
on:
3+
workflow_call:
4+
push:
5+
branches:
6+
- main
7+
pull_request:
8+
jobs:
9+
ci:
10+
name: CI
11+
runs-on: ubuntu-latest
12+
steps:
13+
- uses: actions/checkout@v4
14+
- uses: actions/setup-go@v5
15+
- uses: golangci/golangci-lint-action@v6
16+
with:
17+
version: v1.64
18+
skip-cache: true
19+
- name: test
20+
run: go test ./... -v -race -coverprofile=coverage.txt -covermode=atomic
21+
- name: Send coverage report
22+
uses: codecov/codecov-action@v4
23+
with:
24+
file: coverage.txt

Diff for: .gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
*.asc

Diff for: LICENSE

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2025 Eukarya
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

Diff for: README.md

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# japangeoid-go
2+
3+
日本のジオイド高を計算するための Go ライブラリ / Go library for calculating geoid height in Japan
4+
5+
- 本ライブラリは国土地理院が提供するものではありません。
6+
- 本ライブラリは [ciscorn/japan-geoid](https://github.com/ciscorn/japan-geoid/tree/main) (MIT License)を参考に実装しています。
7+
8+
対応ジオイドモデル:
9+
10+
- 日本のジオイド 2011(Ver.2.2)([出典](https://fgd.gsi.go.jp/download/geoid.php)): `gsigeoid2011`
11+
- 日本のジオイド 2025([出典](https://www.gsi.go.jp/buturisokuchi/grageo_reference.html)): 正式公開後対応予定
12+
13+
```go
14+
package main
15+
16+
import (
17+
"fmt"
18+
"github.com/eukarya-inc/japan-geoid-go/gsigeoid2011"
19+
)
20+
21+
func Example() {
22+
g, err := gsigeoid2011.Load()
23+
if err != nil {
24+
panic(err)
25+
}
26+
27+
lng, lat := 138.2839817085188, 37.12378643088312
28+
height := g.GetHeight(lng, lat)
29+
fmt.Printf("Input: (lng: %.6f, lat: %.6f) -> Geoid height: %.6f\n", lng, lat, height)
30+
31+
lng, lat = 10, 10
32+
height = g.GetHeight(lng, lat)
33+
fmt.Printf("Input: (lng: %.6f, lat: %.6f) -> Geoid height: %.6f\n", lng, lat, height)
34+
35+
// Output:
36+
// Input: (lng: 138.283982, lat: 37.123786) -> Geoid height: 39.473871
37+
// Input: (lng: 10.000000, lat: 10.000000) -> Geoid height: NaN
38+
}
39+
```
40+
41+
## Lisence
42+
43+
[MIT Lisence](LICENSE)

Diff for: cmd/conv.go

+72
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
package main
2+
3+
import (
4+
"compress/gzip"
5+
"io"
6+
"net/http"
7+
"os"
8+
"path"
9+
"strings"
10+
11+
japangeoid "github.com/eukarya-inc/japan-geoid-go"
12+
)
13+
14+
func main() {
15+
if len(os.Args) < 2 {
16+
println("Usage: conv <file>")
17+
os.Exit(1)
18+
}
19+
20+
p := os.Args[1]
21+
name := path.Base(p)
22+
23+
f, err := loadAsc(p)
24+
if err != nil {
25+
println(err.Error())
26+
os.Exit(1)
27+
}
28+
29+
defer f.Close()
30+
31+
grid, err := japangeoid.FromAsc(f)
32+
if err != nil {
33+
println(err.Error())
34+
os.Exit(1)
35+
}
36+
37+
f2, err := os.Create(strings.TrimSuffix(name, ".asc") + ".bin.gz")
38+
if err != nil {
39+
println(err.Error())
40+
os.Exit(1)
41+
}
42+
43+
defer f2.Close()
44+
45+
w := gzip.NewWriter(f2)
46+
defer w.Close()
47+
48+
if err := grid.ToBinary(w); err != nil {
49+
println(err.Error())
50+
os.Exit(1)
51+
}
52+
53+
println("Done")
54+
}
55+
56+
func loadAsc(url string) (io.ReadCloser, error) {
57+
if strings.HasPrefix(url, "http://") || strings.HasPrefix(url, "https://") {
58+
req, err := http.NewRequest("GET", url, nil)
59+
if err != nil {
60+
return nil, err
61+
}
62+
63+
res, err := http.DefaultClient.Do(req)
64+
if err != nil {
65+
return nil, err
66+
}
67+
68+
return res.Body, nil
69+
}
70+
71+
return os.Open(url)
72+
}

Diff for: gird_info.go

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package japangeoid
2+
3+
// GridInfo はジオイドモデルのメタデータを表します。
4+
type GridInfo struct {
5+
XNum uint32 // x方向グリッド数
6+
YNum uint32 // y方向グリッド数
7+
XDenom uint32 // x方向の分母 (0.025→1/0.025=40)
8+
YDenom uint32 // y方向の分母 (0.016667→1/0.016667≈60)
9+
XMin float32 // 経度の最小値
10+
YMin float32 // 緯度の最小値
11+
IKind uint16 // 未使用(ヘッダのikind)
12+
Version string // バージョン文字列
13+
}

Diff for: go.mod

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
module github.com/eukarya-inc/japan-geoid-go
2+
3+
go 1.24.0

Diff for: grid.go

+74
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
package japangeoid
2+
3+
import "math"
4+
5+
// Grid はジオイドデータを表すインターフェースです。
6+
type Grid interface {
7+
// GetHeight は指定した経度(lng)・緯度(lat)におけるジオイドの高さを返します。
8+
GetHeight(lng, lat float64) float64
9+
}
10+
11+
// MemoryGrid はジオイドデータを保持する構造体です。
12+
type MemoryGrid struct {
13+
Info GridInfo
14+
Points []int32 // グリッド点の高さ: [y_num * x_num]
15+
}
16+
17+
// GetHeight は指定した経度(lng)・緯度(lat)におけるジオイドの高さを返します。無効な座標の場合は NaN を返します。
18+
func (g *MemoryGrid) GetHeight(lng, lat float64) float64 {
19+
x, y, info := lng, lat, g.Info
20+
21+
// 1. convert lng, lat to grid index
22+
gridX := (x - float64(info.XMin)) * float64(info.XDenom)
23+
gridY := (y - float64(info.YMin)) * float64(info.YDenom)
24+
if gridX < 0.0 || gridY < 0.0 {
25+
return math.NaN()
26+
}
27+
28+
ix := int(math.Floor(gridX))
29+
iy := int(math.Floor(gridY))
30+
if ix < 0 || iy < 0 || ix >= int(info.XNum) || iy >= int(info.YNum) {
31+
return math.NaN()
32+
}
33+
34+
// 2. get values of 4 points around
35+
v00 := g.lookupGridPoints(ix, iy)
36+
37+
var v01 float64
38+
if ix < int(info.XNum)-1 {
39+
v01 = g.lookupGridPoints(ix+1, iy)
40+
} else {
41+
v01 = math.NaN()
42+
}
43+
44+
var v10 float64
45+
if iy < int(info.YNum)-1 {
46+
v10 = g.lookupGridPoints(ix, iy+1)
47+
} else {
48+
v10 = math.NaN()
49+
}
50+
51+
var v11 float64
52+
if ix < int(info.XNum)-1 && iy < int(info.YNum)-1 {
53+
v11 = g.lookupGridPoints(ix+1, iy+1)
54+
} else {
55+
v11 = math.NaN()
56+
}
57+
58+
// 3. apply bilinear interpolation
59+
xFrac := gridX - float64(ix)
60+
yFrac := gridY - float64(iy)
61+
return bilinear(xFrac, yFrac, v00, v01, v10, v11)
62+
}
63+
64+
func (g *MemoryGrid) lookupGridPoints(ix, iy int) float64 {
65+
pos := iy*int(g.Info.XNum) + ix
66+
v := g.Points[pos]
67+
68+
// 9990000 is NaN
69+
if v == 9990000 {
70+
return math.NaN()
71+
}
72+
73+
return float64(v) / 10000.0
74+
}

Diff for: grid_test.go

+55
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
package japangeoid
2+
3+
import (
4+
"math"
5+
"testing"
6+
)
7+
8+
var _ Grid = (*MemoryGrid)(nil)
9+
10+
func TestMemoryGrid_GetHeight(t *testing.T) {
11+
g := &MemoryGrid{
12+
Info: GridInfo{
13+
XNum: 3,
14+
YNum: 3,
15+
XDenom: 1,
16+
YDenom: 1,
17+
XMin: 0,
18+
YMin: 0,
19+
},
20+
Points: []int32{
21+
0, 1, 2,
22+
3, 4, 5,
23+
6, 7, 8,
24+
},
25+
}
26+
27+
tests := [][]float64{
28+
{0.0, 0.0, 0.0},
29+
{0.0, 1.0, 0.0003},
30+
{1.0, 0.0, 0.0001},
31+
{1.0, 1.0, 0.0004},
32+
{2.0, 0.0, 0.0002},
33+
{0.0, 2.0, 0.0006},
34+
{2.0, 2.0, 0.0008},
35+
{1.5, 2.0, 0.00075},
36+
{-1.0, -1.0, math.NaN()},
37+
{3.0, 3.0, math.NaN()},
38+
}
39+
40+
for _, test := range tests {
41+
x, y, expected := test[0], test[1], test[2]
42+
h := g.GetHeight(x, y)
43+
44+
if math.IsNaN(expected) {
45+
if !math.IsNaN(h) {
46+
t.Errorf("invalid height: %v", h)
47+
}
48+
continue
49+
}
50+
51+
if h != expected {
52+
t.Errorf("invalid height: %v", h)
53+
}
54+
}
55+
}

Diff for: gsigeoid2011/geoid2011.go

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package gsigeoid2011
2+
3+
import (
4+
"bytes"
5+
"compress/gzip"
6+
_ "embed"
7+
"fmt"
8+
9+
japangeoid "github.com/eukarya-inc/japan-geoid-go"
10+
)
11+
12+
//go:embed gsigeo2011_ver2_2.bin.gz
13+
var geoid2011raw []byte
14+
15+
func Load() (*japangeoid.MemoryGrid, error) {
16+
b := bytes.NewReader(geoid2011raw)
17+
g, err := gzip.NewReader(b)
18+
if err != nil {
19+
return nil, fmt.Errorf("failed to create gzip reader: %w", err)
20+
}
21+
22+
return japangeoid.FromBinary(g)
23+
}

Diff for: gsigeoid2011/geoid2011_test.go

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package gsigeoid2011
2+
3+
import (
4+
"fmt"
5+
// "github.com/eukarya-inc/japan-geoid-go/gsigeoid2011"
6+
)
7+
8+
func Example() {
9+
g, err := Load()
10+
// g, err := gsigeoid2011.Load()
11+
if err != nil {
12+
panic(err)
13+
}
14+
15+
lng, lat := 138.2839817085188, 37.12378643088312
16+
height := g.GetHeight(lng, lat)
17+
fmt.Printf("Input: (lng: %.6f, lat: %.6f) -> Geoid height: %.6f\n", lng, lat, height)
18+
19+
lng, lat = 10, 10
20+
height = g.GetHeight(lng, lat)
21+
fmt.Printf("Input: (lng: %.6f, lat: %.6f) -> Geoid height: %.6f\n", lng, lat, height)
22+
23+
// Output:
24+
// Input: (lng: 138.283982, lat: 37.123786) -> Geoid height: 39.473871
25+
// Input: (lng: 10.000000, lat: 10.000000) -> Geoid height: NaN
26+
}

Diff for: gsigeoid2011/gsigeo2011_ver2_2.bin.gz

288 KB
Binary file not shown.

0 commit comments

Comments
 (0)