github.com/liucxer/courier@v1.7.1/h3/h3api_test.go (about) 1 package h3 2 3 import ( 4 "bytes" 5 "io" 6 "io/ioutil" 7 "math" 8 "os" 9 "strconv" 10 "strings" 11 "testing" 12 "text/scanner" 13 14 "github.com/stretchr/testify/require" 15 ) 16 17 func TestH3Api(t *testing.T) { 18 t.Run("geoToH3_res", func(t *testing.T) { 19 anywhere := GeoCoord{0, 0} 20 require.True(t, geoToH3(&anywhere, -1) == 0, "resolution below 0 is invalid") 21 require.True(t, geoToH3(&anywhere, 16) == 0, "resolution above 15 is invalid") 22 }) 23 24 t.Run("geoToH3_coord", func(t *testing.T) { 25 invalidLat := GeoCoord{math.NaN(), 0} 26 invalidLon := GeoCoord{0, math.NaN()} 27 invalidLatLon := GeoCoord{math.Inf(1), math.Inf(-1)} 28 require.True(t, geoToH3(&invalidLat, 1) == 0, "invalid latitude is rejected") 29 require.True(t, geoToH3(&invalidLon, 1) == 0, "invalid longitude is rejected") 30 require.True(t, geoToH3(&invalidLatLon, 1) == 0, "coordinates with infinity are rejected") 31 }) 32 33 t.Run("h3ToGeoBoundary_classIIIEdgeVertex", func(t *testing.T) { 34 // Bug test for https://github.com/uber/h3/issues/45 35 hexes := []string{ 36 "894cc5349b7ffff", "894cc534d97ffff", "894cc53682bffff", 37 "894cc536b17ffff", "894cc53688bffff", "894cead92cbffff", 38 "894cc536537ffff", "894cc5acbabffff", "894cc536597ffff", 39 } 40 numHexes := len(hexes) 41 var h3 H3Index 42 for i := 0; i < numHexes; i++ { 43 h3 = stringToH3(hexes[i]) 44 var b GeoBoundary 45 h3ToGeoBoundary(h3, &b) 46 require.True(t, b.numVerts == 7, "got expected vertex count") 47 } 48 }) 49 50 t.Run("h3ToGeoBoundary_classIIIEdgeVertex_exact", func(t *testing.T) { 51 // Bug test for https://github.com/uber/h3/issues/45 52 h3 := stringToH3("894cc536537ffff") 53 var boundary GeoBoundary 54 boundary.numVerts = 7 55 boundary.Verts = make([]GeoCoord, 7) 56 57 setGeoDegs(&boundary.Verts[0], 18.043333154, -66.27836523500002) 58 setGeoDegs(&boundary.Verts[1], 18.042238363, -66.27929062800001) 59 setGeoDegs(&boundary.Verts[2], 18.040818259, -66.27854193899998) 60 setGeoDegs(&boundary.Verts[3], 18.040492975, -66.27686786700002) 61 setGeoDegs(&boundary.Verts[4], 18.041040385, -66.27640518300001) 62 setGeoDegs(&boundary.Verts[5], 18.041757122, -66.27596711500001) 63 setGeoDegs(&boundary.Verts[6], 18.043007860, -66.27669118199998) 64 assertBoundary(t, h3, &boundary) 65 }) 66 67 t.Run("h3ToGeoBoundary_coslonConstrain", func(t *testing.T) { 68 // Bug test for https://github.com/uber/h3/issues/212 69 h3 := H3Index(0x87dc6d364ffffff) 70 var boundary GeoBoundary 71 boundary.numVerts = 6 72 boundary.Verts = make([]GeoCoord, 6) 73 74 setGeoDegs(&boundary.Verts[0], -52.0130533678236091, -34.6232931343713091) 75 setGeoDegs(&boundary.Verts[1], -52.0041156384652012, -34.6096733160584549) 76 setGeoDegs(&boundary.Verts[2], -51.9929610229502472, -34.6165157145896387) 77 setGeoDegs(&boundary.Verts[3], -51.9907410568096608, -34.6369680004259877) 78 setGeoDegs(&boundary.Verts[4], -51.9996738734672377, -34.6505896528323660) 79 setGeoDegs(&boundary.Verts[5], -52.0108315681413629, -34.6437571897165668) 80 assertBoundary(t, h3, &boundary) 81 }) 82 83 } 84 85 var centerFiles []string 86 var cellFiles []string 87 88 func init() { 89 fileList, err := ioutil.ReadDir("./testdata") 90 if err != nil { 91 panic(err) 92 } 93 94 for _, f := range fileList { 95 name := f.Name() 96 97 if strings.Contains(name, "centers") { 98 centerFiles = append(centerFiles, "./testdata/"+f.Name()) 99 } 100 101 if strings.Contains(name, "cells") { 102 cellFiles = append(cellFiles, "./testdata/"+f.Name()) 103 } 104 } 105 } 106 107 func _Test_testdata(t *testing.T) { 108 t.Run("centers", func(t *testing.T) { 109 for _, f := range centerFiles { 110 data := loadData(f) 111 112 for i, coords := range data { 113 h := i 114 115 if len(coords) == 1 { 116 expectG := &coords[0] 117 118 g := GeoCoord{} 119 h3ToGeo(h, &g) 120 121 require.True(t, geoDegreeEqual(&g, expectG), "h3ToGeo %x %d %v %v", h, h, expectG.AsDegrees(), g.AsDegrees()) 122 require.Equal(t, h, geoToH3(expectG, h3GetResolution(h)), "h3ToGeo %x %d", h, h) 123 } 124 } 125 } 126 }) 127 128 t.Run("cells", func(t *testing.T) { 129 for _, f := range cellFiles { 130 data := loadData(f) 131 132 for i, coords := range data { 133 h := i 134 135 if len(coords) > 0 { 136 expectGB := &GeoBoundary{numVerts: len(coords), Verts: coords} 137 138 assertBoundary(t, h, expectGB) 139 } 140 } 141 } 142 }) 143 } 144 145 func assertBoundary(t *testing.T, h H3Index, expectGB *GeoBoundary) { 146 gb := GeoBoundary{} 147 h3ToGeoBoundary(h, &gb) 148 149 require.Equal(t, expectGB.numVerts, gb.numVerts, "h3ToGeoBoundary %x %v %v", h, gb.AsDegrees(), expectGB.AsDegrees()) 150 151 for i := 0; i < expectGB.numVerts; i++ { 152 require.True(t, geoDegreeEqual(&expectGB.Verts[i], &gb.Verts[i]), "h3ToGeoBoundary %x &v &v", h, expectGB.Verts[i].AsDegrees(), gb.Verts[i].AsDegrees()) 153 } 154 } 155 156 func geoDegreeEqual(p1 *GeoCoord, p2 *GeoCoord) bool { 157 return geoAlmostEqualThreshold(p1.AsDegrees(), p2.AsDegrees(), 0.000001) 158 } 159 160 type expects map[H3Index][]GeoCoord 161 162 func loadData(filename string) expects { 163 f, err := os.Open(filename) 164 if err != nil { 165 panic(err) 166 } 167 defer f.Close() 168 return parseInputs(f) 169 } 170 171 func parseInputs(reader io.Reader) expects { 172 set := expects{} 173 174 var s scanner.Scanner 175 s.Init(reader) 176 177 tmp := bytes.NewBuffer(nil) 178 179 clearTmp := func() { 180 tmp = bytes.NewBuffer(nil) 181 } 182 183 var h uint64 184 var coord *GeoCoord 185 var inCell bool 186 187 for tok := s.Scan(); tok != scanner.EOF; tok = s.Next() { 188 switch tok { 189 case '\n', ' ': 190 if tmp.Len() == 0 { 191 break 192 } 193 194 if h == 0 { 195 h, _ = strconv.ParseUint(tmp.String(), 16, 64) 196 clearTmp() 197 set[H3Index(h)] = []GeoCoord{} 198 break 199 } 200 201 if coord == nil { 202 coord = &GeoCoord{} 203 } 204 205 if coord.Lat == 0 { 206 f, _ := strconv.ParseFloat(tmp.String(), 64) 207 clearTmp() 208 coord.Lat = f * deg2rad 209 break 210 } 211 212 if coord.Lon == 0 { 213 f, _ := strconv.ParseFloat(tmp.String(), 64) 214 clearTmp() 215 coord.Lon = f * deg2rad 216 217 set[H3Index(h)] = append(set[H3Index(h)], *coord) 218 219 // clear 220 coord = nil 221 222 if !inCell { 223 h = 0 224 } 225 } 226 case '{': 227 inCell = true 228 case '}': 229 inCell = false 230 h = 0 231 default: 232 tmp.WriteRune(tok) 233 } 234 } 235 236 return set 237 }