Skip to content

Commit

Permalink
fix default maxzoom; support raw geojson geometries [#68, #64]
Browse files Browse the repository at this point in the history
  • Loading branch information
bdon committed Sep 11, 2023
1 parent c62ee21 commit d803052
Show file tree
Hide file tree
Showing 4 changed files with 99 additions and 19 deletions.
2 changes: 1 addition & 1 deletion main.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ var cli struct {
Output string `arg:"" help:"Output archive." type:"path"`
Bucket string `help:"Remote bucket of input archive."`
Region string `help:"local GeoJSON Polygon or MultiPolygon file for area of interest." type:"existingfile"`
Maxzoom uint8 `help:"Maximum zoom level, inclusive."`
Maxzoom int8 `default:-1 help:"Maximum zoom level, inclusive."`
DownloadThreads int `default:4 help:"Number of download threads."`
DryRun bool `help:"Calculate tiles to extract, but don't download them."`
Overfetch float32 `default:0.1 help:"What ratio of extra data to download to minimize # requests; 0.2 is 20%"`
Expand Down
33 changes: 15 additions & 18 deletions pmtiles/extract.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@ import (
"fmt"
"github.com/RoaringBitmap/roaring/roaring64"
"github.com/dustin/go-humanize"
"github.com/paulmach/orb"
"github.com/paulmach/orb/geojson"
"github.com/schollz/progressbar/v3"
"golang.org/x/sync/errgroup"
"io"
Expand Down Expand Up @@ -250,7 +248,7 @@ func MergeRanges(ranges []SrcDstRange, overfetch float32) (*list.List, uint64) {
// 10. write the leaf directories (if any)
// 11. Get all tiles, and write directly to the output.

func Extract(logger *log.Logger, bucketURL string, key string, maxzoom uint8, region_file string, output string, download_threads int, overfetch float32, dry_run bool) error {
func Extract(logger *log.Logger, bucketURL string, key string, maxzoom int8, region_file string, output string, download_threads int, overfetch float32, dry_run bool) error {
// 1. fetch the header

fmt.Println("WARNING: extract is an experimental feature and results may not be suitable for production use.")
Expand Down Expand Up @@ -294,27 +292,26 @@ func Extract(logger *log.Logger, bucketURL string, key string, maxzoom uint8, re
source_metadata_offset := header.MetadataOffset
source_tile_data_offset := header.TileDataOffset

if header.MaxZoom < maxzoom {
maxzoom = header.MaxZoom
fmt.Println(maxzoom)

if maxzoom == -1 || int8(header.MaxZoom) < maxzoom {
maxzoom = int8(header.MaxZoom)
}

var relevant_set *roaring64.Bitmap
if region_file != "" {

// 2. construct a relevance bitmap
dat, _ := ioutil.ReadFile(region_file)
f, _ := geojson.UnmarshalFeature(dat)

var multipolygon orb.MultiPolygon
switch v := f.Geometry.(type) {
case orb.Polygon:
multipolygon = []orb.Polygon{v}
case orb.MultiPolygon:
multipolygon = v
multipolygon, err := UnmarshalRegion(dat)

if err != nil {
return err
}

bound := multipolygon.Bound()

boundary_set, interior_set := bitmapMultiPolygon(maxzoom, multipolygon)
boundary_set, interior_set := bitmapMultiPolygon(uint8(maxzoom), multipolygon)
relevant_set = boundary_set
relevant_set.Or(interior_set)
generalizeOr(relevant_set)
Expand All @@ -327,7 +324,7 @@ func Extract(logger *log.Logger, bucketURL string, key string, maxzoom uint8, re
header.CenterLatE7 = int32(bound.Center().Y() * 10000000)
} else {
relevant_set = roaring64.New()
relevant_set.AddRange(0, ZxyToId(maxzoom+1, 0, 0))
relevant_set.AddRange(0, ZxyToId(uint8(maxzoom)+1, 0, 0))
}

// 3. get relevant entries from root
Expand All @@ -346,7 +343,7 @@ func Extract(logger *log.Logger, bucketURL string, key string, maxzoom uint8, re

root_dir := deserialize_entries(bytes.NewBuffer(root_bytes))

tile_entries, leaves := RelevantEntries(relevant_set, maxzoom, root_dir)
tile_entries, leaves := RelevantEntries(relevant_set, uint8(maxzoom), root_dir)

// 4. get all relevant leaf entries

Expand Down Expand Up @@ -378,7 +375,7 @@ func Extract(logger *log.Logger, bucketURL string, key string, maxzoom uint8, re
return err
}
leafdir := deserialize_entries(bytes.NewBuffer(leaf_bytes))
new_entries, new_leaves := RelevantEntries(relevant_set, maxzoom, leafdir)
new_entries, new_leaves := RelevantEntries(relevant_set, uint8(maxzoom), leafdir)

if len(new_leaves) > 0 {
panic("This doesn't support leaf level 2+.")
Expand Down Expand Up @@ -425,7 +422,7 @@ func Extract(logger *log.Logger, bucketURL string, key string, maxzoom uint8, re
header.TileEntriesCount = uint64(len(tile_entries))
header.TileContentsCount = tile_contents

header.MaxZoom = maxzoom
header.MaxZoom = uint8(maxzoom)

header_bytes := serialize_header(header)

Expand Down
35 changes: 35 additions & 0 deletions pmtiles/region.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package pmtiles

import (
"fmt"
"github.com/paulmach/orb"
"github.com/paulmach/orb/geojson"
)

func UnmarshalRegion(data []byte) (orb.MultiPolygon, error) {
f, err := geojson.UnmarshalFeature(data)

if err == nil {
switch v := f.Geometry.(type) {
case orb.Polygon:
return []orb.Polygon{v}, nil
case orb.MultiPolygon:
return v, nil
}
}

g, err := geojson.UnmarshalGeometry(data)

if err != nil {
return nil, err
}

switch v := g.Geometry().(type) {
case orb.Polygon:
return []orb.Polygon{v}, nil
case orb.MultiPolygon:
return v, nil
}

return nil, fmt.Errorf("No geometry")
}
48 changes: 48 additions & 0 deletions pmtiles/region_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package pmtiles

import (
"github.com/stretchr/testify/assert"
"testing"
)

func TestRawPolygonRegion(t *testing.T) {
result, err := UnmarshalRegion([]byte(`{
"type": "Polygon",
"coordinates": [[[0, 0],[0,1],[1,1],[0,0]]]
}`))
assert.Nil(t, err)
assert.Equal(t, 1, len(result))
}

func TestRawMultiPolygonRegion(t *testing.T) {
result, err := UnmarshalRegion([]byte(`{
"type": "MultiPolygon",
"coordinates": [[[[0, 0],[0,1],[1,1],[0,0]]]]
}`))
assert.Nil(t, err)
assert.Equal(t, 1, len(result))
}

func TestRawPolygonFeatureRegion(t *testing.T) {
result, err := UnmarshalRegion([]byte(`{
"type": "Feature",
"geometry": {
"type": "Polygon",
"coordinates": [[[0, 0],[0,1],[1,1],[0,0]]]
}
}`))
assert.Nil(t, err)
assert.Equal(t, 1, len(result))
}

func TestRawMultiPolygonFeatureRegion(t *testing.T) {
result, err := UnmarshalRegion([]byte(`{
"type": "Feature",
"geometry": {
"type": "MultiPolygon",
"coordinates": [[[[0, 0],[0,1],[1,1],[0,0]]]]
}
}`))
assert.Nil(t, err)
assert.Equal(t, 1, len(result))
}

0 comments on commit d803052

Please sign in to comment.