tile38/geojson/levels.go

557 lines
12 KiB
Go

package geojson
import (
"bytes"
"encoding/binary"
)
////////////////////////////////
// level 1
////////////////////////////////
func fillLevel1Map(m map[string]interface{}) (
coordinates Position, bbox *BBox, bytesOut []byte, err error,
) {
switch v := m["coordinates"].(type) {
default:
err = errInvalidCoordinates
return
case nil:
err = errCoordinatesRequired
return
case []interface{}:
coordinates, err = fillPosition(v)
}
if err == nil {
bbox, err = fillBBox(m)
}
return
}
func fillLevel1Bytes(b []byte, bbox *BBox, isCordZ bool) (
coordinates Position, bboxOut *BBox, bytesOut []byte, err error,
) {
bboxOut = bbox
coordinates, bytesOut, err = fillPositionBytes(b, isCordZ)
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
}
func level1JSON(name string, coordinates Position, bbox *BBox) string {
isCordZ := level1IsCoordZDefined(coordinates, bbox)
var buf bytes.Buffer
buf.WriteString(`{"type":"`)
buf.WriteString(name)
buf.WriteString(`","coordinates":[`)
coordinates.writeJSON(&buf, isCordZ)
buf.WriteByte(']')
bbox.write(&buf)
buf.WriteByte('}')
return buf.String()
}
func level1IsCoordZDefined(coordinates Position, bbox *BBox) bool {
if bbox.isCordZDefined() {
return true
}
return coordinates.Z != nilz
}
func level1Bytes(objType byte, coordinates Position, bbox *BBox) []byte {
var buf bytes.Buffer
isCordZ := level1IsCoordZDefined(coordinates, bbox)
writeHeader(&buf, objType, bbox, isCordZ)
coordinates.writeBytes(&buf, isCordZ)
return buf.Bytes()
}
////////////////////////////////
// level 2
////////////////////////////////
func fillLevel2Map(m map[string]interface{}) (
coordinates []Position, bbox *BBox, bytesOut []byte, err error,
) {
switch v := m["coordinates"].(type) {
default:
err = errInvalidCoordinates
return
case nil:
err = errCoordinatesRequired
return
case []interface{}:
coordinates = make([]Position, len(v))
for i, v := range v {
v, ok := v.([]interface{})
if !ok {
err = errCoordinatesMustBeArray
return
}
var p Position
p, err = fillPosition(v)
if err != nil {
return
}
coordinates[i] = p
}
}
bbox, err = fillBBox(m)
return
}
func fillLevel2Bytes(b []byte, bbox *BBox, isCordZ bool) (
coordinates []Position, bboxOut *BBox, bytesOut []byte, err error,
) {
bboxOut = bbox
if len(b) < 4 {
err = errNotEnoughData
return
}
coordinates = make([]Position, int(binary.LittleEndian.Uint32(b)))
b = b[4:]
for i := 0; i < len(coordinates); i++ {
coordinates[i], b, err = fillPositionBytes(b, isCordZ)
if err != nil {
return
}
}
bytesOut = b
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
}
func level2JSON(name string, coordinates []Position, bbox *BBox) string {
isCordZ := level2IsCoordZDefined(coordinates, bbox)
var buf bytes.Buffer
buf.WriteString(`{"type":"`)
buf.WriteString(name)
buf.WriteString(`","coordinates":[`)
for i, p := range coordinates {
if i > 0 {
buf.WriteByte(',')
}
buf.WriteByte('[')
p.writeJSON(&buf, isCordZ)
buf.WriteByte(']')
}
buf.WriteByte(']')
bbox.write(&buf)
buf.WriteByte('}')
return buf.String()
}
func level2IsCoordZDefined(coordinates []Position, bbox *BBox) bool {
if bbox.isCordZDefined() {
return true
}
for _, p := range coordinates {
if p.Z != nilz {
return true
}
}
return false
}
func level2Bytes(objType byte, coordinates []Position, bbox *BBox) []byte {
var buf bytes.Buffer
isCordZ := level2IsCoordZDefined(coordinates, bbox)
writeHeader(&buf, objType, bbox, isCordZ)
b := make([]byte, 4)
binary.LittleEndian.PutUint32(b, uint32(len(coordinates)))
buf.Write(b)
for _, p := range coordinates {
p.writeBytes(&buf, isCordZ)
}
return buf.Bytes()
}
////////////////////////////////
// level 3
////////////////////////////////
func fillLevel3Map(m map[string]interface{}) (
coordinates [][]Position, bbox *BBox, bytesOut []byte, err error,
) {
switch v := m["coordinates"].(type) {
default:
err = errInvalidCoordinates
return
case nil:
err = errCoordinatesRequired
return
case []interface{}:
coordinates = make([][]Position, len(v))
for i, v := range v {
v, ok := v.([]interface{})
if !ok {
err = errInvalidCoordinatesValue
return
}
ps := make([]Position, len(v))
for i, v := range v {
v, ok := v.([]interface{})
if !ok {
err = errInvalidCoordinatesValue
return
}
var p Position
p, err = fillPosition(v)
if err != nil {
return
}
ps[i] = p
}
coordinates[i] = ps
}
}
bbox, err = fillBBox(m)
return
}
func fillLevel3Bytes(b []byte, bbox *BBox, isCordZ bool) (
coordinates [][]Position, bboxOut *BBox, bytesOut []byte, err error,
) {
bboxOut = bbox
if len(b) < 4 {
err = errNotEnoughData
return
}
coordinates = make([][]Position, int(binary.LittleEndian.Uint32(b)))
b = b[4:]
for i := 0; i < len(coordinates); i++ {
if len(b) < 4 {
err = errNotEnoughData
return
}
ps := make([]Position, int(binary.LittleEndian.Uint32(b)))
b = b[4:]
for j := 0; j < len(ps); j++ {
ps[j], b, err = fillPositionBytes(b, isCordZ)
if err != nil {
return
}
}
coordinates[i] = ps
}
bytesOut = b
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
}
func level3JSON(name string, coordinates [][]Position, bbox *BBox) string {
isCordZ := level3IsCoordZDefined(coordinates, bbox)
var buf bytes.Buffer
buf.WriteString(`{"type":"`)
buf.WriteString(name)
buf.WriteString(`","coordinates":[`)
for i, p := range coordinates {
if i > 0 {
buf.WriteByte(',')
}
buf.WriteByte('[')
for i, p := range p {
if i > 0 {
buf.WriteByte(',')
}
buf.WriteByte('[')
p.writeJSON(&buf, isCordZ)
buf.WriteByte(']')
}
buf.WriteByte(']')
}
buf.WriteByte(']')
bbox.write(&buf)
buf.WriteByte('}')
return buf.String()
}
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
}
func level3Bytes(objType byte, coordinates [][]Position, bbox *BBox) []byte {
var buf bytes.Buffer
isCordZ := level3IsCoordZDefined(coordinates, bbox)
writeHeader(&buf, objType, bbox, isCordZ)
b := make([]byte, 4)
binary.LittleEndian.PutUint32(b, uint32(len(coordinates)))
buf.Write(b)
for _, p := range coordinates {
binary.LittleEndian.PutUint32(b, uint32(len(p)))
buf.Write(b)
for _, p := range p {
p.writeBytes(&buf, isCordZ)
}
}
return buf.Bytes()
}
////////////////////////////////
// level 4
////////////////////////////////
func fillLevel4Map(m map[string]interface{}) (
coordinates [][][]Position, bbox *BBox, bytesOut []byte, err error,
) {
switch v := m["coordinates"].(type) {
default:
err = errInvalidCoordinates
return
case nil:
err = errCoordinatesRequired
return
case []interface{}:
coordinates = make([][][]Position, len(v))
for i, v := range v {
v, ok := v.([]interface{})
if !ok {
err = errInvalidCoordinatesValue
return
}
ps := make([][]Position, len(v))
for i, v := range v {
v, ok := v.([]interface{})
if !ok {
err = errInvalidCoordinatesValue
return
}
pss := make([]Position, len(v))
for i, v := range v {
v, ok := v.([]interface{})
if !ok {
err = errInvalidCoordinatesValue
return
}
var p Position
p, err = fillPosition(v)
if err != nil {
return
}
pss[i] = p
}
ps[i] = pss
}
coordinates[i] = ps
}
}
bbox, err = fillBBox(m)
return
}
func fillLevel4Bytes(b []byte, bbox *BBox, isCordZ bool) (
coordinates [][][]Position, bboxOut *BBox, bytesOut []byte, err error,
) {
bboxOut = bbox
if len(b) < 4 {
err = errNotEnoughData
return
}
coordinates = make([][][]Position, int(binary.LittleEndian.Uint32(b)))
b = b[4:]
for i := 0; i < len(coordinates); i++ {
if len(b) < 4 {
err = errNotEnoughData
return
}
ps := make([][]Position, int(binary.LittleEndian.Uint32(b)))
b = b[4:]
for i := 0; i < len(ps); i++ {
if len(b) < 4 {
err = errNotEnoughData
return
}
pss := make([]Position, int(binary.LittleEndian.Uint32(b)))
b = b[4:]
for i := 0; i < len(pss); i++ {
pss[i], b, err = fillPositionBytes(b, isCordZ)
if err != nil {
return
}
}
ps[i] = pss
}
coordinates[i] = ps
}
bytesOut = b
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
}
func level4JSON(name string, coordinates [][][]Position, bbox *BBox) string {
isCordZ := level4IsCoordZDefined(coordinates, bbox)
var buf bytes.Buffer
buf.WriteString(`{"type":"`)
buf.WriteString(name)
buf.WriteString(`","coordinates":[`)
for i, p := range coordinates {
if i > 0 {
buf.WriteByte(',')
}
buf.WriteByte('[')
for i, p := range p {
if i > 0 {
buf.WriteByte(',')
}
buf.WriteByte('[')
for i, p := range p {
if i > 0 {
buf.WriteByte(',')
}
buf.WriteByte('[')
p.writeJSON(&buf, isCordZ)
buf.WriteByte(']')
}
buf.WriteByte(']')
}
buf.WriteByte(']')
}
buf.WriteByte(']')
bbox.write(&buf)
buf.WriteByte('}')
return buf.String()
}
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
}
func level4Bytes(objType byte, coordinates [][][]Position, bbox *BBox) []byte {
var buf bytes.Buffer
isCordZ := level4IsCoordZDefined(coordinates, bbox)
writeHeader(&buf, objType, bbox, isCordZ)
b := make([]byte, 4)
binary.LittleEndian.PutUint32(b, uint32(len(coordinates)))
buf.Write(b)
for _, p := range coordinates {
binary.LittleEndian.PutUint32(b, uint32(len(p)))
buf.Write(b)
for _, p := range p {
binary.LittleEndian.PutUint32(b, uint32(len(p)))
buf.Write(b)
for _, p := range p {
p.writeBytes(&buf, isCordZ)
}
}
}
return buf.Bytes()
}