-
Notifications
You must be signed in to change notification settings - Fork 6
/
layer.go
123 lines (112 loc) · 3.77 KB
/
layer.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
package tilenol
import (
"bytes"
"context"
"crypto/sha256"
"encoding/gob"
"errors"
"fmt"
"github.com/paulmach/orb/geojson"
)
var (
MultipleSourcesErr = errors.New("Layers can only support a single backend source")
NoSourcesErr = errors.New("Layers must have a single backend source configured")
LayerMinZoomOutOfBoundsErr = errors.New("Layer Min zoom is below absolute min zoom")
LayerMaxZoomOutOfBoundsErr = errors.New("Layer Max Zoom is above absolute max zoom")
)
// SourceConfig represents a generic YAML source configuration object
type SourceConfig struct {
// Elasticsearch is an optional YAML key for configuring an ElasticsearchConfig
Elasticsearch *ElasticsearchConfig `yaml:"elasticsearch"`
// PostGIS is an optional YAML key for configuring a PostGISConfig
PostGIS *PostGISConfig `yaml:"postgis"`
}
// LayerConfig represents a general YAML layer configuration object
type LayerConfig struct {
// Name is the effective name of the layer
Name string `yaml:"name"`
// Description is an optional short descriptor of the layer
Description string `yaml:"description"`
// Minzoom specifies the minimum z value for the layer
Minzoom int `yaml:"minzoom"`
// Maxzoom specifies the maximum z value for the layer
Maxzoom int `yaml:"maxzoom"`
// NoCache indicates that this layer should not cache its source data
NoCache bool `yaml:"nocache"`
// Source configures the underlying Source for the layer
Source SourceConfig `yaml:"source"`
}
// Source is a generic interface for all feature data sources
type Source interface {
// GetFeatures retrieves the GeoJSON FeatureCollection for the given request
GetFeatures(context.Context, *TileRequest) (*geojson.FeatureCollection, error)
}
// Layer is a configured, hydrated tile server layer
type Layer struct {
Name string
Description string
Minzoom int
Maxzoom int
Cacheable bool
source Source // Note that source is not exported to avoid encoding issues
}
// CreateLayer creates a new Layer given a LayerConfig
func CreateLayer(layerConfig LayerConfig) (*Layer, error) {
layer := &Layer{
Name: layerConfig.Name,
Description: layerConfig.Description,
Minzoom: layerConfig.Minzoom,
Maxzoom: layerConfig.Maxzoom,
Cacheable: !layerConfig.NoCache,
}
// TODO: How can we make this more generic?
if layerConfig.Source.Elasticsearch != nil && layerConfig.Source.PostGIS != nil {
return nil, MultipleSourcesErr
}
if layerConfig.Source.Elasticsearch == nil && layerConfig.Source.PostGIS == nil {
return nil, NoSourcesErr
}
if layerConfig.Minzoom < MinZoom {
return nil, LayerMinZoomOutOfBoundsErr
}
if layerConfig.Maxzoom > MaxZoom {
return nil, LayerMaxZoomOutOfBoundsErr
}
if layerConfig.Source.Elasticsearch != nil {
source, err := NewElasticsearchSource(layerConfig.Source.Elasticsearch)
if err != nil {
return nil, err
}
layer.source = source
return layer, nil
}
if layerConfig.Source.PostGIS != nil {
source, err := NewPostGISSource(layerConfig.Source.PostGIS)
if err != nil {
return nil, err
}
layer.source = source
return layer, nil
}
return nil, fmt.Errorf("Invalid layer source config for layer: %s", layerConfig.Name)
}
// GetFeatures implements a passthrough interface to the layer's underlying source
func (l Layer) GetFeatures(ctx context.Context, r *TileRequest) (*geojson.FeatureCollection, error) {
return l.source.GetFeatures(ctx, r)
}
// Hash computes a content-based SHA256 digest to diff layer "versions"
func (l Layer) Hash() string {
var buf bytes.Buffer
enc := gob.NewEncoder(&buf)
err := enc.Encode(l)
if err != nil {
panic(err)
}
hash := sha256.New()
hash.Write(buf.Bytes())
hashBytes := hash.Sum(nil)
return fmt.Sprintf("%x", hashBytes)
}
func (l Layer) String() string {
return fmt.Sprintf("%s@%s", l.Name, l.Hash())
}