grect.go 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337
  1. package grect
  2. import (
  3. "strconv"
  4. "strings"
  5. "github.com/tidwall/gjson"
  6. )
  7. type Rect struct {
  8. Min, Max []float64
  9. }
  10. func (r Rect) String() string {
  11. diff := len(r.Min) != len(r.Max)
  12. if !diff {
  13. for i := 0; i < len(r.Min); i++ {
  14. if r.Min[i] != r.Max[i] {
  15. diff = true
  16. break
  17. }
  18. }
  19. }
  20. var buf []byte
  21. buf = append(buf, '[')
  22. for i, v := range r.Min {
  23. if i > 0 {
  24. buf = append(buf, ' ')
  25. }
  26. buf = append(buf, strconv.FormatFloat(v, 'f', -1, 64)...)
  27. }
  28. if diff {
  29. buf = append(buf, ']', ',', '[')
  30. for i, v := range r.Max {
  31. if i > 0 {
  32. buf = append(buf, ' ')
  33. }
  34. buf = append(buf, strconv.FormatFloat(v, 'f', -1, 64)...)
  35. }
  36. }
  37. buf = append(buf, ']')
  38. return string(buf)
  39. }
  40. func normalize(min, max []float64) (nmin, nmax []float64) {
  41. if len(max) == 0 {
  42. return min, min
  43. } else if len(max) != len(min) {
  44. if len(max) < len(min) {
  45. max = append(max, min[len(max):]...)
  46. } else if len(min) < len(max) {
  47. min = append(min, max[len(min):]...)
  48. }
  49. }
  50. match := true
  51. for i := 0; i < len(min); i++ {
  52. if min[i] != max[i] {
  53. if match {
  54. match = false
  55. }
  56. if min[i] > max[i] {
  57. min[i], max[i] = max[i], min[i]
  58. }
  59. }
  60. }
  61. if match {
  62. return min, min
  63. }
  64. return min, max
  65. }
  66. func Get(s string) Rect {
  67. var i int
  68. var ws bool
  69. var min, max []float64
  70. for ; i < len(s); i++ {
  71. switch s[i] {
  72. default:
  73. continue
  74. case ' ', '\t', '\r', '\n':
  75. ws = true
  76. continue
  77. case '[':
  78. min, max, i = getRect(s, i)
  79. case '{':
  80. min, max, i = getGeoJSON(s, i)
  81. case 0x00, 0x01:
  82. if !ws {
  83. // return parseWKB(s, i)
  84. }
  85. case 'p', 'P', 'l', 'L', 'm', 'M', 'g', 'G':
  86. min, max, i = getWKT(s, i)
  87. }
  88. break
  89. }
  90. min, max = normalize(min, max)
  91. return Rect{Min: min, Max: max}
  92. }
  93. func getRect(s string, i int) (min, max []float64, ri int) {
  94. a := s[i:]
  95. parts := strings.Split(a, ",")
  96. for i := 0; i < len(parts) && i < 2; i++ {
  97. part := parts[i]
  98. if len(part) > 0 && (part[0] <= ' ' || part[len(part)-1] <= ' ') {
  99. part = strings.TrimSpace(part)
  100. }
  101. if len(part) >= 2 && part[0] == '[' && part[len(part)-1] == ']' {
  102. pieces := strings.Split(part[1:len(part)-1], " ")
  103. if i == 0 {
  104. min = make([]float64, 0, len(pieces))
  105. } else {
  106. max = make([]float64, 0, len(pieces))
  107. }
  108. for j := 0; j < len(pieces); j++ {
  109. piece := pieces[j]
  110. if piece != "" {
  111. n, _ := strconv.ParseFloat(piece, 64)
  112. if i == 0 {
  113. min = append(min, n)
  114. } else {
  115. max = append(max, n)
  116. }
  117. }
  118. }
  119. }
  120. }
  121. // normalize
  122. if len(parts) == 1 {
  123. max = min
  124. } else {
  125. min, max = normalize(min, max)
  126. }
  127. return min, max, len(s)
  128. }
  129. func union(min1, max1, min2, max2 []float64) (umin, umax []float64) {
  130. for i := 0; i < len(min1) || i < len(min2); i++ {
  131. if i >= len(min1) {
  132. // just copy min2
  133. umin = append(umin, min2[i])
  134. umax = append(umax, max2[i])
  135. } else if i >= len(min2) {
  136. // just copy min1
  137. umin = append(umin, min1[i])
  138. umax = append(umax, max1[i])
  139. } else {
  140. if min1[i] < min2[i] {
  141. umin = append(umin, min1[i])
  142. } else {
  143. umin = append(umin, min2[i])
  144. }
  145. if max1[i] > max2[i] {
  146. umax = append(umax, max1[i])
  147. } else {
  148. umax = append(umax, max2[i])
  149. }
  150. }
  151. }
  152. return umin, umax
  153. }
  154. func getWKT(s string, i int) (min, max []float64, ri int) {
  155. switch s[i] {
  156. default:
  157. for ; i < len(s); i++ {
  158. if s[i] == ',' {
  159. return nil, nil, i
  160. }
  161. if s[i] == '(' {
  162. return getWKTAny(s, i)
  163. }
  164. }
  165. return nil, nil, i
  166. case 'g', 'G':
  167. if len(s)-i < 18 {
  168. return nil, nil, i
  169. }
  170. return getWKTGeometryCollection(s, i+18)
  171. }
  172. }
  173. func getWKTAny(s string, i int) (min, max []float64, ri int) {
  174. min, max = make([]float64, 0, 4), make([]float64, 0, 4)
  175. var depth int
  176. var ni int
  177. var idx int
  178. loop:
  179. for ; i < len(s); i++ {
  180. switch s[i] {
  181. default:
  182. if ni == 0 {
  183. ni = i
  184. }
  185. case '(':
  186. depth++
  187. case ')', ' ', '\t', '\r', '\n', ',':
  188. if ni != 0 {
  189. n, _ := strconv.ParseFloat(s[ni:i], 64)
  190. if idx >= len(min) {
  191. min = append(min, n)
  192. max = append(max, n)
  193. } else {
  194. if n < min[idx] {
  195. min[idx] = n
  196. } else if n > max[idx] {
  197. max[idx] = n
  198. }
  199. }
  200. idx++
  201. ni = 0
  202. }
  203. switch s[i] {
  204. case ')':
  205. idx = 0
  206. depth--
  207. if depth == 0 {
  208. i++
  209. break loop
  210. }
  211. case ',':
  212. idx = 0
  213. }
  214. }
  215. }
  216. return min, max, i
  217. }
  218. func getWKTGeometryCollection(s string, i int) (min, max []float64, ri int) {
  219. var depth int
  220. for ; i < len(s); i++ {
  221. if s[i] == ',' || s[i] == ')' {
  222. // do not increment the index
  223. return nil, nil, i
  224. }
  225. if s[i] == '(' {
  226. depth++
  227. i++
  228. break
  229. }
  230. }
  231. next:
  232. for ; i < len(s); i++ {
  233. switch s[i] {
  234. case 'p', 'P', 'l', 'L', 'm', 'M', 'g', 'G':
  235. var min2, max2 []float64
  236. min2, max2, i = getWKT(s, i)
  237. min, max = union(min, max, min2, max2)
  238. for ; i < len(s); i++ {
  239. if s[i] == ',' {
  240. i++
  241. goto next
  242. }
  243. if s[i] == ')' {
  244. i++
  245. goto done
  246. }
  247. }
  248. case ' ', '\t', '\r', '\n':
  249. continue
  250. default:
  251. goto end_early
  252. }
  253. }
  254. end_early:
  255. // just balance the parens
  256. for ; i < len(s); i++ {
  257. if s[i] == '(' {
  258. depth++
  259. } else if s[i] == ')' {
  260. depth--
  261. if depth == 0 {
  262. i++
  263. break
  264. }
  265. }
  266. }
  267. done:
  268. return min, max, i
  269. }
  270. func getGeoJSON(s string, i int) (min, max []float64, ri int) {
  271. json := s[i:]
  272. switch gjson.Get(json, "type").String() {
  273. default:
  274. min, max = getMinMaxBrackets(gjson.Get(json, "coordinates").Raw)
  275. case "Feature":
  276. min, max, _ = getGeoJSON(gjson.Get(json, "geometry").String(), 0)
  277. case "FeatureCollection":
  278. for _, json := range gjson.Get(json, "features").Array() {
  279. nmin, nmax, _ := getGeoJSON(json.String(), 0)
  280. min, max = union(min, max, nmin, nmax)
  281. }
  282. case "GeometryCollection":
  283. for _, json := range gjson.Get(json, "geometries").Array() {
  284. nmin, nmax, _ := getGeoJSON(json.String(), 0)
  285. min, max = union(min, max, nmin, nmax)
  286. }
  287. }
  288. return min, max, len(json)
  289. }
  290. func getMinMaxBrackets(s string) (min, max []float64) {
  291. var ni int
  292. var idx int
  293. for i := 0; i < len(s); i++ {
  294. switch s[i] {
  295. default:
  296. if ni == 0 {
  297. ni = i
  298. }
  299. case '[', ',', ']', ' ', '\t', '\r', '\n':
  300. if ni > 0 {
  301. n, _ := strconv.ParseFloat(s[ni:i], 64)
  302. if idx >= len(min) {
  303. min = append(min, n)
  304. max = append(max, n)
  305. } else {
  306. if n < min[idx] {
  307. min[idx] = n
  308. } else if n > max[idx] {
  309. max[idx] = n
  310. }
  311. }
  312. ni = 0
  313. idx++
  314. }
  315. if s[i] == ']' {
  316. idx = 0
  317. }
  318. }
  319. }
  320. return
  321. }