-
Notifications
You must be signed in to change notification settings - Fork 7
/
topology.go
152 lines (123 loc) · 3.36 KB
/
topology.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
package topojson
import (
"encoding/json"
"github.com/paulmach/go.geojson"
)
type RecuctionAlgoritm int
const (
Visvalingam RecuctionAlgoritm = 0
DouglasPeucker RecuctionAlgoritm = 1
Radial RecuctionAlgoritm = 2
RadialGeo RecuctionAlgoritm = 3
)
type Topology struct {
Type string `json:"type"`
Transform *Transform `json:"transform,omitempty"`
BoundingBox []float64 `json:"bbox,omitempty"`
Objects map[string]*Geometry `json:"objects"`
Arcs [][][]float64 `json:"arcs"`
// For internal use only
opts *TopologyOptions
input []*geojson.Feature
coordinates [][]float64
objects []*topologyObject
lines []*arc
rings []*arc
arcs []*arc
arcIndexes map[arcEntry]int
deletedArcs map[int]bool
shiftArcs map[int]int
}
type Transform struct {
Scale [2]float64 `json:"scale"`
Translate [2]float64 `json:"translate"`
}
type TopologyOptions struct {
// Pre-quantization precision
PreQuantize float64
// Post-quantization precision
PostQuantize float64
// Maximum simplification error, set to 0 to disable
Simplify float64
// ID property key
IDProperty string
// Reduction algorithm
ReductionAlgorithm RecuctionAlgoritm
}
func NewTopology(fc *geojson.FeatureCollection, opts *TopologyOptions) *Topology {
if opts == nil {
opts = &TopologyOptions{}
}
if opts.IDProperty == "" {
opts.IDProperty = "id"
}
topo := &Topology{
Type: "Topology",
input: fc.Features,
opts: opts,
Objects: make(map[string]*Geometry),
}
topo.bounds()
topo.preQuantize()
topo.extract()
topo.join()
topo.cut()
topo.dedup()
topo.unpackArcs()
topo.simplify()
topo.unpackObjects()
topo.removeEmpty()
topo.postQuantize()
topo.delta()
// No longer needed
topo.opts = nil
return topo
}
// MarshalJSON converts the topology object into the proper JSON.
// It will handle the encoding of all the child geometries.
// Alternately one can call json.Marshal(t) directly for the same result.
func (t *Topology) MarshalJSON() ([]byte, error) {
t.Type = "Topology"
if t.Objects == nil {
t.Objects = make(map[string]*Geometry) // TopoJSON requires the objects attribute to be at least {}
}
if t.Arcs == nil {
t.Arcs = make([][][]float64, 0) // TopoJSON requires the arcs attribute to be at least []
}
return json.Marshal(*t)
}
// UnmarshalTopology decodes the data into a TopoJSON topology.
// Alternately one can call json.Unmarshal(topo) directly for the same result.
func UnmarshalTopology(data []byte) (*Topology, error) {
topo := &Topology{}
err := json.Unmarshal(data, topo)
if err != nil {
return nil, err
}
return topo, nil
}
// Internal structs
type arc struct {
Start int
End int
Next *arc
}
type point [2]float64
func newPoint(coords []float64) point {
return point{coords[0], coords[1]}
}
func pointEquals(a, b []float64) bool {
return a != nil && b != nil && len(a) == len(b) && a[0] == b[0] && a[1] == b[1]
}
type topologyObject struct {
ID string
Type geojson.GeometryType
Properties map[string]interface{}
BoundingBox []float64
Geometries []*topologyObject // For geometry collections
Arc *arc // For lines
Arcs []*arc // For multi lines and polygons
MultiArcs [][]*arc // For multi polygons
Point []float64 // for points
MultiPoint [][]float64 // for multi points
}