1package geojson
2
3import "github.com/tidwall/gjson"
4
5func resIsArray(res gjson.Result) bool {
6	if res.Type == gjson.JSON {
7		for i := 0; i < len(res.Raw); i++ {
8			if res.Raw[i] == '[' {
9				return true
10			}
11			if res.Raw[i] <= ' ' {
12				continue
13			}
14			break
15		}
16	}
17	return false
18}
19
20////////////////////////////////
21// level 1
22////////////////////////////////
23
24func fillLevel1Map(json string) (
25	coordinates Position, bbox *BBox, err error,
26) {
27	coords := gjson.Get(json, "coordinates")
28	switch coords.Type {
29	default:
30		err = errInvalidCoordinates
31		return
32	case gjson.Null:
33		err = errCoordinatesRequired
34		return
35	case gjson.JSON:
36		if !resIsArray(coords) {
37			err = errInvalidCoordinates
38			return
39		}
40		coordinates, err = fillPosition(coords)
41		if err != nil {
42			return
43		}
44	}
45	bbox, err = fillBBox(json)
46	return
47}
48
49func level1CalculatedBBox(coordinates Position, bbox *BBox) BBox {
50	if bbox != nil {
51		return *bbox
52	}
53	return BBox{
54		Min: coordinates,
55		Max: coordinates,
56	}
57}
58
59func level1PositionCount(coordinates Position, bbox *BBox) int {
60	if bbox != nil {
61		return 3
62	}
63	return 1
64}
65
66func level1Weight(coordinates Position, bbox *BBox) int {
67	return level1PositionCount(coordinates, bbox) * sizeofPosition
68}
69
70func appendLevel1JSON(json []byte, name string, coordinates Position, bbox *BBox, bboxDefined bool) []byte {
71	if bbox != nil && !bboxDefined {
72		bbox = nil
73	}
74	isCordZ := level1IsCoordZDefined(coordinates, bbox)
75	json = append(json, `{"type":"`...)
76	json = append(json, name...)
77	json = append(json, `","coordinates":[`...)
78	json = appendPositionJSON(json, coordinates, isCordZ)
79	json = append(json, ']')
80	if bboxDefined {
81		json = appendBBoxJSON(json, bbox)
82	}
83	return append(json, '}')
84}
85
86func level1IsCoordZDefined(coordinates Position, bbox *BBox) bool {
87	if bbox.isCordZDefined() {
88		return true
89	}
90	return coordinates.Z != nilz
91}
92
93////////////////////////////////
94// level 2
95////////////////////////////////
96
97func fillLevel2Map(json string) (
98	coordinates []Position, bbox *BBox, err error,
99) {
100	coords := gjson.Get(json, "coordinates")
101	switch coords.Type {
102	default:
103		err = errInvalidCoordinates
104		return
105	case gjson.Null:
106		err = errCoordinatesRequired
107		return
108	case gjson.JSON:
109		if !resIsArray(coords) {
110			err = errInvalidCoordinates
111			return
112		}
113		v := coords.Array()
114		coordinates = make([]Position, len(v))
115		for i, coords := range v {
116			if !resIsArray(coords) {
117				err = errInvalidCoordinates
118				return
119			}
120			var p Position
121			p, err = fillPosition(coords)
122			if err != nil {
123				return
124			}
125			coordinates[i] = p
126		}
127	}
128	bbox, err = fillBBox(json)
129	return
130}
131
132func level2CalculatedBBox(coordinates []Position, bbox *BBox) BBox {
133	if bbox != nil {
134		return *bbox
135	}
136	_, bbox2 := positionBBox(0, BBox{}, coordinates)
137	return bbox2
138}
139
140func level2PositionCount(coordinates []Position, bbox *BBox) int {
141	if bbox != nil {
142		return 2 + len(coordinates)
143	}
144	return len(coordinates)
145}
146
147func level2Weight(coordinates []Position, bbox *BBox) int {
148	return level2PositionCount(coordinates, bbox) * sizeofPosition
149}
150
151func appendLevel2JSON(json []byte, name string, coordinates []Position, bbox *BBox, bboxDefined bool) []byte {
152	if bbox != nil && !bboxDefined {
153		bbox = nil
154	}
155	isCordZ := level2IsCoordZDefined(coordinates, bbox)
156	json = append(json, `{"type":"`...)
157	json = append(json, name...)
158	json = append(json, `","coordinates":[`...)
159	for i, p := range coordinates {
160		if i > 0 {
161			json = append(json, ',')
162		}
163		json = append(json, '[')
164		json = appendPositionJSON(json, p, isCordZ)
165		json = append(json, ']')
166	}
167	json = append(json, ']')
168	if bboxDefined {
169		json = appendBBoxJSON(json, bbox)
170	}
171	json = append(json, '}')
172	return json
173}
174
175func level2IsCoordZDefined(coordinates []Position, bbox *BBox) bool {
176	if bbox.isCordZDefined() {
177		return true
178	}
179	for _, p := range coordinates {
180		if p.Z != nilz {
181			return true
182		}
183	}
184	return false
185}
186
187////////////////////////////////
188// level 3
189////////////////////////////////
190
191func fillLevel3Map(json string) (
192	coordinates [][]Position, bbox *BBox, err error,
193) {
194	coords := gjson.Get(json, "coordinates")
195	switch coords.Type {
196	default:
197		err = errInvalidCoordinates
198		return
199	case gjson.Null:
200		err = errCoordinatesRequired
201		return
202	case gjson.JSON:
203		if !resIsArray(coords) {
204			err = errInvalidCoordinates
205			return
206		}
207		v := coords.Array()
208		coordinates = make([][]Position, len(v))
209		for i, coords := range v {
210			if !resIsArray(coords) {
211				err = errInvalidCoordinates
212				return
213			}
214			v := coords.Array()
215			ps := make([]Position, len(v))
216			for i, coords := range v {
217				if !resIsArray(coords) {
218					err = errInvalidCoordinates
219					return
220				}
221				var p Position
222				p, err = fillPosition(coords)
223				if err != nil {
224					return
225				}
226				ps[i] = p
227			}
228			coordinates[i] = ps
229		}
230	}
231	bbox, err = fillBBox(json)
232	return
233}
234
235func level3CalculatedBBox(coordinates [][]Position, bbox *BBox, isPolygon bool) BBox {
236	if bbox != nil {
237		return *bbox
238	}
239	var bbox2 BBox
240	var i = 0
241	for _, ps := range coordinates {
242		i, bbox2 = positionBBox(i, bbox2, ps)
243		if isPolygon {
244			break // only the exterior ring should be calculated for a polygon
245		}
246	}
247	return bbox2
248}
249
250func level3Weight(coordinates [][]Position, bbox *BBox) int {
251	return level3PositionCount(coordinates, bbox) * sizeofPosition
252}
253
254func level3PositionCount(coordinates [][]Position, bbox *BBox) int {
255	var res int
256	for _, p := range coordinates {
257		res += len(p)
258	}
259	if bbox != nil {
260		return 2 + res
261	}
262	return res
263}
264
265func appendLevel3JSON(json []byte, name string, coordinates [][]Position, bbox *BBox, bboxDefined bool) []byte {
266	if bbox != nil && !bboxDefined {
267		bbox = nil
268	}
269	isCordZ := level3IsCoordZDefined(coordinates, bbox)
270	json = append(json, `{"type":"`...)
271	json = append(json, name...)
272	json = append(json, `","coordinates":[`...)
273	for i, p := range coordinates {
274		if i > 0 {
275			json = append(json, ',')
276		}
277		json = append(json, '[')
278		for i, p := range p {
279			if i > 0 {
280				json = append(json, ',')
281			}
282			json = append(json, '[')
283			json = appendPositionJSON(json, p, isCordZ)
284			json = append(json, ']')
285		}
286		json = append(json, ']')
287	}
288	json = append(json, ']')
289	if bboxDefined {
290		json = appendBBoxJSON(json, bbox)
291	}
292	return append(json, '}')
293}
294
295func level3IsCoordZDefined(coordinates [][]Position, bbox *BBox) bool {
296	if bbox.isCordZDefined() {
297		return true
298	}
299	for _, p := range coordinates {
300		for _, p := range p {
301			if p.Z != nilz {
302				return true
303			}
304		}
305	}
306	return false
307}
308
309////////////////////////////////
310// level 4
311////////////////////////////////
312
313func fillLevel4Map(json string) (
314	coordinates [][][]Position, bbox *BBox, err error,
315) {
316	coords := gjson.Get(json, "coordinates")
317	switch coords.Type {
318	default:
319		err = errInvalidCoordinates
320		return
321	case gjson.Null:
322		err = errCoordinatesRequired
323		return
324	case gjson.JSON:
325		if !resIsArray(coords) {
326			err = errInvalidCoordinates
327			return
328		}
329		v := coords.Array()
330		coordinates = make([][][]Position, len(v))
331		for i, coords := range v {
332			if !resIsArray(coords) {
333				err = errInvalidCoordinates
334				return
335			}
336			v := coords.Array()
337			ps := make([][]Position, len(v))
338			for i, coords := range v {
339				if !resIsArray(coords) {
340					err = errInvalidCoordinates
341					return
342				}
343				v := coords.Array()
344				pss := make([]Position, len(v))
345				for i, coords := range v {
346					if !resIsArray(coords) {
347						err = errInvalidCoordinates
348						return
349					}
350					var p Position
351					p, err = fillPosition(coords)
352					if err != nil {
353						return
354					}
355					pss[i] = p
356				}
357				ps[i] = pss
358			}
359			coordinates[i] = ps
360		}
361	}
362	bbox, err = fillBBox(json)
363	return
364}
365
366func level4Weight(coordinates [][][]Position, bbox *BBox) int {
367	return level4PositionCount(coordinates, bbox) * sizeofPosition
368}
369
370func level4PositionCount(coordinates [][][]Position, bbox *BBox) int {
371	var res int
372	for _, p := range coordinates {
373		for _, p := range p {
374			res += len(p)
375		}
376	}
377	if bbox != nil {
378		return 2 + res
379	}
380	return res
381}
382
383func appendLevel4JSON(json []byte, name string, coordinates [][][]Position, bbox *BBox, bboxDefined bool) []byte {
384	if bbox != nil && !bboxDefined {
385		bbox = nil
386	}
387	isCordZ := level4IsCoordZDefined(coordinates, bbox)
388	json = append(json, `{"type":"`...)
389	json = append(json, name...)
390	json = append(json, `","coordinates":[`...)
391	for i, p := range coordinates {
392		if i > 0 {
393			json = append(json, ',')
394		}
395		json = append(json, '[')
396		for i, p := range p {
397			if i > 0 {
398				json = append(json, ',')
399			}
400			json = append(json, '[')
401			for i, p := range p {
402				if i > 0 {
403					json = append(json, ',')
404				}
405				json = append(json, '[')
406				json = appendPositionJSON(json, p, isCordZ)
407				json = append(json, ']')
408			}
409			json = append(json, ']')
410		}
411		json = append(json, ']')
412	}
413	json = append(json, ']')
414	if bboxDefined {
415		json = appendBBoxJSON(json, bbox)
416	}
417	return append(json, '}')
418}
419
420func level4IsCoordZDefined(coordinates [][][]Position, bbox *BBox) bool {
421	if bbox.isCordZDefined() {
422		return true
423	}
424	for _, p := range coordinates {
425		for _, p := range p {
426			for _, p := range p {
427				if p.Z != nilz {
428					return true
429				}
430			}
431		}
432	}
433	return false
434}
435