2016-03-05 02:08:16 +03:00
|
|
|
package geojson
|
|
|
|
|
2018-01-11 23:57:18 +03:00
|
|
|
import "github.com/tidwall/gjson"
|
2016-03-05 02:08:16 +03:00
|
|
|
|
2016-11-02 15:51:48 +03:00
|
|
|
func resIsArray(res gjson.Result) bool {
|
|
|
|
if res.Type == gjson.JSON {
|
|
|
|
for i := 0; i < len(res.Raw); i++ {
|
|
|
|
if res.Raw[i] == '[' {
|
|
|
|
return true
|
|
|
|
}
|
2016-12-17 00:02:58 +03:00
|
|
|
if res.Raw[i] <= ' ' {
|
|
|
|
continue
|
|
|
|
}
|
2016-11-02 15:51:48 +03:00
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2016-03-05 02:08:16 +03:00
|
|
|
////////////////////////////////
|
|
|
|
// level 1
|
|
|
|
////////////////////////////////
|
|
|
|
|
2016-11-02 15:51:48 +03:00
|
|
|
func fillLevel1Map(json string) (
|
2016-12-17 00:02:58 +03:00
|
|
|
coordinates Position, bbox *BBox, err error,
|
2016-03-05 02:08:16 +03:00
|
|
|
) {
|
2016-11-02 15:51:48 +03:00
|
|
|
coords := gjson.Get(json, "coordinates")
|
|
|
|
switch coords.Type {
|
2016-03-05 02:08:16 +03:00
|
|
|
default:
|
|
|
|
err = errInvalidCoordinates
|
|
|
|
return
|
2016-11-02 15:51:48 +03:00
|
|
|
case gjson.Null:
|
2016-03-05 02:08:16 +03:00
|
|
|
err = errCoordinatesRequired
|
|
|
|
return
|
2016-11-02 15:51:48 +03:00
|
|
|
case gjson.JSON:
|
|
|
|
if !resIsArray(coords) {
|
|
|
|
err = errInvalidCoordinates
|
|
|
|
return
|
|
|
|
}
|
|
|
|
coordinates, err = fillPosition(coords)
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
2016-03-05 02:08:16 +03:00
|
|
|
}
|
2016-11-02 15:51:48 +03:00
|
|
|
bbox, err = fillBBox(json)
|
2016-03-05 02:08:16 +03:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func level1CalculatedBBox(coordinates Position, bbox *BBox) BBox {
|
|
|
|
if bbox != nil {
|
|
|
|
return *bbox
|
|
|
|
}
|
|
|
|
return BBox{
|
|
|
|
Min: coordinates,
|
|
|
|
Max: coordinates,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func level1PositionCount(coordinates Position, bbox *BBox) int {
|
|
|
|
if bbox != nil {
|
|
|
|
return 3
|
|
|
|
}
|
|
|
|
return 1
|
|
|
|
}
|
|
|
|
|
|
|
|
func level1Weight(coordinates Position, bbox *BBox) int {
|
|
|
|
return level1PositionCount(coordinates, bbox) * sizeofPosition
|
|
|
|
}
|
|
|
|
|
2018-01-11 23:57:18 +03:00
|
|
|
func appendLevel1JSON(json []byte, name string, coordinates Position, bbox *BBox, bboxDefined bool) []byte {
|
|
|
|
if bbox != nil && !bboxDefined {
|
|
|
|
bbox = nil
|
|
|
|
}
|
2016-03-05 02:08:16 +03:00
|
|
|
isCordZ := level1IsCoordZDefined(coordinates, bbox)
|
2018-01-11 23:57:18 +03:00
|
|
|
json = append(json, `{"type":"`...)
|
|
|
|
json = append(json, name...)
|
|
|
|
json = append(json, `","coordinates":[`...)
|
|
|
|
json = appendPositionJSON(json, coordinates, isCordZ)
|
|
|
|
json = append(json, ']')
|
|
|
|
if bboxDefined {
|
|
|
|
json = appendBBoxJSON(json, bbox)
|
|
|
|
}
|
|
|
|
return append(json, '}')
|
2016-03-05 02:08:16 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
func level1IsCoordZDefined(coordinates Position, bbox *BBox) bool {
|
|
|
|
if bbox.isCordZDefined() {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
return coordinates.Z != nilz
|
|
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////
|
|
|
|
// level 2
|
|
|
|
////////////////////////////////
|
|
|
|
|
2016-11-02 15:51:48 +03:00
|
|
|
func fillLevel2Map(json string) (
|
2016-12-17 00:02:58 +03:00
|
|
|
coordinates []Position, bbox *BBox, err error,
|
2016-03-05 02:08:16 +03:00
|
|
|
) {
|
2016-11-02 15:51:48 +03:00
|
|
|
coords := gjson.Get(json, "coordinates")
|
|
|
|
switch coords.Type {
|
2016-03-05 02:08:16 +03:00
|
|
|
default:
|
|
|
|
err = errInvalidCoordinates
|
|
|
|
return
|
2016-11-02 15:51:48 +03:00
|
|
|
case gjson.Null:
|
2016-03-05 02:08:16 +03:00
|
|
|
err = errCoordinatesRequired
|
|
|
|
return
|
2016-11-02 15:51:48 +03:00
|
|
|
case gjson.JSON:
|
|
|
|
if !resIsArray(coords) {
|
|
|
|
err = errInvalidCoordinates
|
|
|
|
return
|
|
|
|
}
|
|
|
|
v := coords.Array()
|
2016-03-05 02:08:16 +03:00
|
|
|
coordinates = make([]Position, len(v))
|
2016-11-02 15:51:48 +03:00
|
|
|
for i, coords := range v {
|
|
|
|
if !resIsArray(coords) {
|
|
|
|
err = errInvalidCoordinates
|
2016-03-05 02:08:16 +03:00
|
|
|
return
|
|
|
|
}
|
|
|
|
var p Position
|
2016-11-02 15:51:48 +03:00
|
|
|
p, err = fillPosition(coords)
|
2016-03-05 02:08:16 +03:00
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
coordinates[i] = p
|
|
|
|
}
|
|
|
|
}
|
2016-11-02 15:51:48 +03:00
|
|
|
bbox, err = fillBBox(json)
|
2016-03-05 02:08:16 +03:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func level2CalculatedBBox(coordinates []Position, bbox *BBox) BBox {
|
|
|
|
if bbox != nil {
|
|
|
|
return *bbox
|
|
|
|
}
|
|
|
|
_, bbox2 := positionBBox(0, BBox{}, coordinates)
|
|
|
|
return bbox2
|
|
|
|
}
|
|
|
|
|
|
|
|
func level2PositionCount(coordinates []Position, bbox *BBox) int {
|
|
|
|
if bbox != nil {
|
|
|
|
return 2 + len(coordinates)
|
|
|
|
}
|
|
|
|
return len(coordinates)
|
|
|
|
}
|
|
|
|
|
|
|
|
func level2Weight(coordinates []Position, bbox *BBox) int {
|
|
|
|
return level2PositionCount(coordinates, bbox) * sizeofPosition
|
|
|
|
}
|
|
|
|
|
2018-01-11 23:57:18 +03:00
|
|
|
func appendLevel2JSON(json []byte, name string, coordinates []Position, bbox *BBox, bboxDefined bool) []byte {
|
|
|
|
if bbox != nil && !bboxDefined {
|
|
|
|
bbox = nil
|
|
|
|
}
|
2016-03-05 02:08:16 +03:00
|
|
|
isCordZ := level2IsCoordZDefined(coordinates, bbox)
|
2018-01-11 23:57:18 +03:00
|
|
|
json = append(json, `{"type":"`...)
|
|
|
|
json = append(json, name...)
|
|
|
|
json = append(json, `","coordinates":[`...)
|
2016-03-05 02:08:16 +03:00
|
|
|
for i, p := range coordinates {
|
|
|
|
if i > 0 {
|
2018-01-11 23:57:18 +03:00
|
|
|
json = append(json, ',')
|
2016-03-05 02:08:16 +03:00
|
|
|
}
|
2018-01-11 23:57:18 +03:00
|
|
|
json = append(json, '[')
|
|
|
|
json = appendPositionJSON(json, p, isCordZ)
|
|
|
|
json = append(json, ']')
|
|
|
|
}
|
|
|
|
json = append(json, ']')
|
|
|
|
if bboxDefined {
|
|
|
|
json = appendBBoxJSON(json, bbox)
|
|
|
|
}
|
|
|
|
json = append(json, '}')
|
|
|
|
return json
|
2016-03-05 02:08:16 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
func level2IsCoordZDefined(coordinates []Position, bbox *BBox) bool {
|
|
|
|
if bbox.isCordZDefined() {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
for _, p := range coordinates {
|
|
|
|
if p.Z != nilz {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////
|
|
|
|
// level 3
|
|
|
|
////////////////////////////////
|
|
|
|
|
2016-11-02 15:51:48 +03:00
|
|
|
func fillLevel3Map(json string) (
|
2016-12-17 00:02:58 +03:00
|
|
|
coordinates [][]Position, bbox *BBox, err error,
|
2016-03-05 02:08:16 +03:00
|
|
|
) {
|
2016-11-02 15:51:48 +03:00
|
|
|
coords := gjson.Get(json, "coordinates")
|
|
|
|
switch coords.Type {
|
2016-03-05 02:08:16 +03:00
|
|
|
default:
|
|
|
|
err = errInvalidCoordinates
|
|
|
|
return
|
2016-11-02 15:51:48 +03:00
|
|
|
case gjson.Null:
|
2016-03-05 02:08:16 +03:00
|
|
|
err = errCoordinatesRequired
|
|
|
|
return
|
2016-11-02 15:51:48 +03:00
|
|
|
case gjson.JSON:
|
|
|
|
if !resIsArray(coords) {
|
|
|
|
err = errInvalidCoordinates
|
|
|
|
return
|
|
|
|
}
|
|
|
|
v := coords.Array()
|
2016-03-05 02:08:16 +03:00
|
|
|
coordinates = make([][]Position, len(v))
|
2016-11-02 15:51:48 +03:00
|
|
|
for i, coords := range v {
|
|
|
|
if !resIsArray(coords) {
|
|
|
|
err = errInvalidCoordinates
|
2016-03-05 02:08:16 +03:00
|
|
|
return
|
|
|
|
}
|
2016-11-02 15:51:48 +03:00
|
|
|
v := coords.Array()
|
2016-03-05 02:08:16 +03:00
|
|
|
ps := make([]Position, len(v))
|
2016-11-02 15:51:48 +03:00
|
|
|
for i, coords := range v {
|
|
|
|
if !resIsArray(coords) {
|
|
|
|
err = errInvalidCoordinates
|
2016-03-05 02:08:16 +03:00
|
|
|
return
|
|
|
|
}
|
|
|
|
var p Position
|
2016-11-02 15:51:48 +03:00
|
|
|
p, err = fillPosition(coords)
|
2016-03-05 02:08:16 +03:00
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
ps[i] = p
|
|
|
|
}
|
|
|
|
coordinates[i] = ps
|
|
|
|
}
|
|
|
|
}
|
2016-11-02 15:51:48 +03:00
|
|
|
bbox, err = fillBBox(json)
|
2016-03-05 02:08:16 +03:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func level3CalculatedBBox(coordinates [][]Position, bbox *BBox, isPolygon bool) BBox {
|
|
|
|
if bbox != nil {
|
|
|
|
return *bbox
|
|
|
|
}
|
|
|
|
var bbox2 BBox
|
|
|
|
var i = 0
|
|
|
|
for _, ps := range coordinates {
|
|
|
|
i, bbox2 = positionBBox(i, bbox2, ps)
|
|
|
|
if isPolygon {
|
|
|
|
break // only the exterior ring should be calculated for a polygon
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return bbox2
|
|
|
|
}
|
|
|
|
|
|
|
|
func level3Weight(coordinates [][]Position, bbox *BBox) int {
|
|
|
|
return level3PositionCount(coordinates, bbox) * sizeofPosition
|
|
|
|
}
|
|
|
|
|
|
|
|
func level3PositionCount(coordinates [][]Position, bbox *BBox) int {
|
|
|
|
var res int
|
|
|
|
for _, p := range coordinates {
|
|
|
|
res += len(p)
|
|
|
|
}
|
|
|
|
if bbox != nil {
|
|
|
|
return 2 + res
|
|
|
|
}
|
|
|
|
return res
|
|
|
|
}
|
|
|
|
|
2018-01-11 23:57:18 +03:00
|
|
|
func appendLevel3JSON(json []byte, name string, coordinates [][]Position, bbox *BBox, bboxDefined bool) []byte {
|
|
|
|
if bbox != nil && !bboxDefined {
|
|
|
|
bbox = nil
|
|
|
|
}
|
2016-03-05 02:08:16 +03:00
|
|
|
isCordZ := level3IsCoordZDefined(coordinates, bbox)
|
2018-01-11 23:57:18 +03:00
|
|
|
json = append(json, `{"type":"`...)
|
|
|
|
json = append(json, name...)
|
|
|
|
json = append(json, `","coordinates":[`...)
|
2016-03-05 02:08:16 +03:00
|
|
|
for i, p := range coordinates {
|
|
|
|
if i > 0 {
|
2018-01-11 23:57:18 +03:00
|
|
|
json = append(json, ',')
|
2016-03-05 02:08:16 +03:00
|
|
|
}
|
2018-01-11 23:57:18 +03:00
|
|
|
json = append(json, '[')
|
2016-03-05 02:08:16 +03:00
|
|
|
for i, p := range p {
|
|
|
|
if i > 0 {
|
2018-01-11 23:57:18 +03:00
|
|
|
json = append(json, ',')
|
2016-03-05 02:08:16 +03:00
|
|
|
}
|
2018-01-11 23:57:18 +03:00
|
|
|
json = append(json, '[')
|
|
|
|
json = appendPositionJSON(json, p, isCordZ)
|
|
|
|
json = append(json, ']')
|
2016-03-05 02:08:16 +03:00
|
|
|
}
|
2018-01-11 23:57:18 +03:00
|
|
|
json = append(json, ']')
|
|
|
|
}
|
|
|
|
json = append(json, ']')
|
|
|
|
if bboxDefined {
|
|
|
|
json = appendBBoxJSON(json, bbox)
|
2016-03-05 02:08:16 +03:00
|
|
|
}
|
2018-01-11 23:57:18 +03:00
|
|
|
return append(json, '}')
|
2016-03-05 02:08:16 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
func level3IsCoordZDefined(coordinates [][]Position, bbox *BBox) bool {
|
|
|
|
if bbox.isCordZDefined() {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
for _, p := range coordinates {
|
|
|
|
for _, p := range p {
|
|
|
|
if p.Z != nilz {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////
|
|
|
|
// level 4
|
|
|
|
////////////////////////////////
|
|
|
|
|
2016-11-02 15:51:48 +03:00
|
|
|
func fillLevel4Map(json string) (
|
2016-12-17 00:02:58 +03:00
|
|
|
coordinates [][][]Position, bbox *BBox, err error,
|
2016-03-05 02:08:16 +03:00
|
|
|
) {
|
2016-11-02 15:51:48 +03:00
|
|
|
coords := gjson.Get(json, "coordinates")
|
|
|
|
switch coords.Type {
|
2016-03-05 02:08:16 +03:00
|
|
|
default:
|
|
|
|
err = errInvalidCoordinates
|
|
|
|
return
|
2016-11-02 15:51:48 +03:00
|
|
|
case gjson.Null:
|
2016-03-05 02:08:16 +03:00
|
|
|
err = errCoordinatesRequired
|
|
|
|
return
|
2016-11-02 15:51:48 +03:00
|
|
|
case gjson.JSON:
|
|
|
|
if !resIsArray(coords) {
|
|
|
|
err = errInvalidCoordinates
|
|
|
|
return
|
|
|
|
}
|
|
|
|
v := coords.Array()
|
2016-03-05 02:08:16 +03:00
|
|
|
coordinates = make([][][]Position, len(v))
|
2016-11-02 15:51:48 +03:00
|
|
|
for i, coords := range v {
|
|
|
|
if !resIsArray(coords) {
|
|
|
|
err = errInvalidCoordinates
|
2016-03-05 02:08:16 +03:00
|
|
|
return
|
|
|
|
}
|
2016-11-02 15:51:48 +03:00
|
|
|
v := coords.Array()
|
2016-03-05 02:08:16 +03:00
|
|
|
ps := make([][]Position, len(v))
|
2016-11-02 15:51:48 +03:00
|
|
|
for i, coords := range v {
|
|
|
|
if !resIsArray(coords) {
|
|
|
|
err = errInvalidCoordinates
|
2016-03-05 02:08:16 +03:00
|
|
|
return
|
|
|
|
}
|
2016-11-02 15:51:48 +03:00
|
|
|
v := coords.Array()
|
2016-03-05 02:08:16 +03:00
|
|
|
pss := make([]Position, len(v))
|
2016-11-02 15:51:48 +03:00
|
|
|
for i, coords := range v {
|
|
|
|
if !resIsArray(coords) {
|
|
|
|
err = errInvalidCoordinates
|
2016-03-05 02:08:16 +03:00
|
|
|
return
|
|
|
|
}
|
|
|
|
var p Position
|
2016-11-02 15:51:48 +03:00
|
|
|
p, err = fillPosition(coords)
|
2016-03-05 02:08:16 +03:00
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
pss[i] = p
|
|
|
|
}
|
|
|
|
ps[i] = pss
|
|
|
|
}
|
|
|
|
coordinates[i] = ps
|
|
|
|
}
|
|
|
|
}
|
2016-11-02 15:51:48 +03:00
|
|
|
bbox, err = fillBBox(json)
|
2016-03-05 02:08:16 +03:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func level4CalculatedBBox(coordinates [][][]Position, bbox *BBox) BBox {
|
|
|
|
if bbox != nil {
|
|
|
|
return *bbox
|
|
|
|
}
|
|
|
|
var bbox2 BBox
|
|
|
|
var i = 0
|
|
|
|
for _, ps := range coordinates {
|
|
|
|
for _, ps := range ps {
|
|
|
|
i, bbox2 = positionBBox(i, bbox2, ps)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return bbox2
|
|
|
|
}
|
|
|
|
|
|
|
|
func level4Weight(coordinates [][][]Position, bbox *BBox) int {
|
|
|
|
return level4PositionCount(coordinates, bbox) * sizeofPosition
|
|
|
|
}
|
|
|
|
|
|
|
|
func level4PositionCount(coordinates [][][]Position, bbox *BBox) int {
|
|
|
|
var res int
|
|
|
|
for _, p := range coordinates {
|
|
|
|
for _, p := range p {
|
|
|
|
res += len(p)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if bbox != nil {
|
|
|
|
return 2 + res
|
|
|
|
}
|
|
|
|
return res
|
|
|
|
}
|
|
|
|
|
2018-01-11 23:57:18 +03:00
|
|
|
func appendLevel4JSON(json []byte, name string, coordinates [][][]Position, bbox *BBox, bboxDefined bool) []byte {
|
|
|
|
if bbox != nil && !bboxDefined {
|
|
|
|
bbox = nil
|
|
|
|
}
|
2016-03-05 02:08:16 +03:00
|
|
|
isCordZ := level4IsCoordZDefined(coordinates, bbox)
|
2018-01-11 23:57:18 +03:00
|
|
|
json = append(json, `{"type":"`...)
|
|
|
|
json = append(json, name...)
|
|
|
|
json = append(json, `","coordinates":[`...)
|
2016-03-05 02:08:16 +03:00
|
|
|
for i, p := range coordinates {
|
|
|
|
if i > 0 {
|
2018-01-11 23:57:18 +03:00
|
|
|
json = append(json, ',')
|
2016-03-05 02:08:16 +03:00
|
|
|
}
|
2018-01-11 23:57:18 +03:00
|
|
|
json = append(json, '[')
|
2016-03-05 02:08:16 +03:00
|
|
|
for i, p := range p {
|
|
|
|
if i > 0 {
|
2018-01-11 23:57:18 +03:00
|
|
|
json = append(json, ',')
|
2016-03-05 02:08:16 +03:00
|
|
|
}
|
2018-01-11 23:57:18 +03:00
|
|
|
json = append(json, '[')
|
2016-03-05 02:08:16 +03:00
|
|
|
for i, p := range p {
|
|
|
|
if i > 0 {
|
2018-01-11 23:57:18 +03:00
|
|
|
json = append(json, ',')
|
2016-03-05 02:08:16 +03:00
|
|
|
}
|
2018-01-11 23:57:18 +03:00
|
|
|
json = append(json, '[')
|
|
|
|
json = appendPositionJSON(json, p, isCordZ)
|
|
|
|
json = append(json, ']')
|
2016-03-05 02:08:16 +03:00
|
|
|
}
|
2018-01-11 23:57:18 +03:00
|
|
|
json = append(json, ']')
|
2016-03-05 02:08:16 +03:00
|
|
|
}
|
2018-01-11 23:57:18 +03:00
|
|
|
json = append(json, ']')
|
|
|
|
}
|
|
|
|
json = append(json, ']')
|
|
|
|
if bboxDefined {
|
|
|
|
json = appendBBoxJSON(json, bbox)
|
2016-03-05 02:08:16 +03:00
|
|
|
}
|
2018-01-11 23:57:18 +03:00
|
|
|
return append(json, '}')
|
2016-03-05 02:08:16 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
func level4IsCoordZDefined(coordinates [][][]Position, bbox *BBox) bool {
|
|
|
|
if bbox.isCordZDefined() {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
for _, p := range coordinates {
|
|
|
|
for _, p := range p {
|
|
|
|
for _, p := range p {
|
|
|
|
if p.Z != nilz {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|