Skip to content

Commit

Permalink
topojson
Browse files Browse the repository at this point in the history
  • Loading branch information
gertcuykens committed Aug 9, 2019
1 parent c6e407b commit 4c1c5cd
Show file tree
Hide file tree
Showing 32 changed files with 6,284 additions and 0 deletions.
1 change: 1 addition & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ go:
- 'tip'

install:
- go get -v github.com/cheekybits/is
- go get -v github.com/paulmach/orb/...
- go install -v
- go test -i ./...
Expand Down
4 changes: 4 additions & 0 deletions encoding/topojson/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# topojson - TopoJSON implementation in Go

Implements the TopoJSON specification:
https://github.com/mbostock/topojson-specification
96 changes: 96 additions & 0 deletions encoding/topojson/bounds.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
package topojson

import (
"math"

"github.com/paulmach/orb"
)

func (t *Topology) bounds() {
t.BBox = []float64{
math.MaxFloat64,
math.MaxFloat64,
-math.MaxFloat64,
-math.MaxFloat64,
}

for _, f := range t.input {
t.boundGeometry(f.Geometry)
}

}

func (t *Topology) boundGeometry(g orb.Geometry) {
switch c := g.(type) {
case orb.Point:
t.Bound(c.Bound())
case orb.MultiPoint:
t.Bound(c.Bound())
case orb.LineString:
t.Bound(c.Bound())
case orb.MultiLineString:
t.Bound(c.Bound())
case orb.Polygon:
t.Bound(c.Bound())
case orb.MultiPolygon:
t.Bound(c.Bound())
case orb.Collection:
t.Bound(c.Bound())
// for _, geo := range c {
// t.boundGeometry(geo)
// }
}
}

func (t *Topology) Bound(b orb.Bound) {
xx := []float64{b.Min[0], b.Max[0]}
yy := []float64{b.Min[1], b.Max[1]}
for _, x := range xx {
if x < t.BBox[0] {
t.BBox[0] = x
}
if x > t.BBox[2] {
t.BBox[2] = x
}
}
for _, y := range yy {
if y < t.BBox[1] {
t.BBox[1] = y
}
if y > t.BBox[3] {
t.BBox[3] = y
}
}
}

func (t *Topology) boundPoint(p []float64) {
x := p[0]
y := p[1]

if x < t.BBox[0] {
t.BBox[0] = x
}
if x > t.BBox[2] {
t.BBox[2] = x
}
if y < t.BBox[1] {
t.BBox[1] = y
}
if y > t.BBox[3] {
t.BBox[3] = y
}
}

func (t *Topology) boundPoints(l [][]float64) {
for _, p := range l {
t.boundPoint(p)
}
}

func (t *Topology) boundMultiPoints(ml [][][]float64) {
for _, l := range ml {
for _, p := range l {
t.boundPoint(p)
}
}
}
27 changes: 27 additions & 0 deletions encoding/topojson/bounds_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package topojson

import (
"testing"

"github.com/cheekybits/is"
orb "github.com/paulmach/orb"
geojson "github.com/paulmach/orb/geojson"
)

func TestBBox(t *testing.T) {
is := is.New(t)

in := []*geojson.Feature{
NewTestFeature("foo", orb.LineString{
orb.Point{0, 0}, orb.Point{1, 0}, orb.Point{2, 0},
}),
NewTestFeature("bar", orb.LineString{
orb.Point{-1, 0}, orb.Point{1, 0}, orb.Point{-2, 3},
}),
}

topo := &Topology{input: in}
topo.bounds()

is.Equal(topo.BBox, []float64{-2, 0, 2, 3})
}
59 changes: 59 additions & 0 deletions encoding/topojson/cut.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package topojson

func (t *Topology) cut() {
junctions := t.join()

for _, line := range t.lines {
mid := line.Start
end := line.End

mid += 1
for mid < end {
if junctions.Has(t.coordinates[mid]) {
next := &arc{Start: mid, End: line.End}
line.End = mid
line.Next = next
line = next
}
mid += 1
}
}

for _, ring := range t.rings {
start := ring.Start
mid := start
end := ring.End
fixed := junctions.Has(t.coordinates[start])

mid += 1
for mid < end {
if junctions.Has(t.coordinates[mid]) {
if fixed {
next := &arc{Start: mid, End: ring.End}
ring.End = mid
ring.Next = next
ring = next
} else {
// For the first junction, we can rotate rather than cut
t.rotateCoordinates(start, end, end-mid)
t.coordinates[end] = t.coordinates[start]
fixed = true
mid = start // restart; we may have skipped junctions
}
}
mid += 1
}
}
}

func (t *Topology) rotateCoordinates(start, end, offset int) {
t.reverseCoordinates(start, end)
t.reverseCoordinates(start, start+offset)
t.reverseCoordinates(start+offset, end)
}

func (t *Topology) reverseCoordinates(start, end int) {
for i, j := start, end; i < j; i, j = i+1, j-1 {
t.coordinates[i], t.coordinates[j] = t.coordinates[j], t.coordinates[i]
}
}
Loading

0 comments on commit 4c1c5cd

Please sign in to comment.