This repository has been archived by the owner on Aug 27, 2020. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 2
/
handle_map_style.go
172 lines (148 loc) · 4.62 KB
/
handle_map_style.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
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
package server
import (
"encoding/json"
"net/http"
"net/url"
"strconv"
"strings"
"github.com/dimfeld/httptreemux"
"gopkg.in/go-playground/colors.v1"
"github.com/go-spatial/geom"
"github.com/go-spatial/tegola-postgis/atlas"
"github.com/go-spatial/tegola-postgis/internal/log"
"github.com/go-spatial/tegola-postgis/mapbox/style"
)
type HandleMapStyle struct {
// required
mapName string
// the requests extension defaults to "json"
extension string
}
// returns details about a map according to the
// tileJSON spec (https://github.com/mapbox/tilejson-spec/tree/master/2.1.0)
//
// URI scheme: /capabilities/:map_name.json
// map_name - map name in the config file
func (req HandleMapStyle) ServeHTTP(w http.ResponseWriter, r *http.Request) {
var err error
params := httptreemux.ContextParams(r.Context())
// read the map_name value from the request
mapName := params["map_name"]
mapNameParts := strings.Split(mapName, ".")
req.mapName = mapNameParts[0]
// check if we have a provided extension
if len(mapNameParts) > 2 {
req.extension = mapNameParts[len(mapNameParts)-1]
} else {
req.extension = "json"
}
// lookup our Map
m, err := atlas.GetMap(req.mapName)
if err != nil {
log.Errorf("map (%v) not configured. check your config file", req.mapName)
http.Error(w, "map ("+req.mapName+") not configured. check your config file", http.StatusNotFound)
return
}
// if we have a debug param add it to our URLs
debugQuery := url.Values{}
if r.URL.Query().Get("debug") == "true" {
debugQuery.Set("debug", "true")
// update our map to include the debug layers
m = m.AddDebugLayers()
}
mapboxStyle := style.Root{
Name: m.Name,
Version: style.Version,
Center: [2]float64{m.Center[0], m.Center[1]},
Zoom: m.Center[2],
Sources: map[string]style.Source{
req.mapName: {
Type: style.SourceTypeVector,
URL: buildCapabilitiesURL(r, []string{"capabilities", req.mapName + ".json"}, debugQuery),
},
},
Layers: []style.Layer{},
}
// determining the min and max zoom for this map
for _, l := range m.Layers {
// check if the layer already exists in our slice. this can happen if the config
// is using the "name" param for a layer to override the providerLayerName
var skip bool
for i := range mapboxStyle.Layers {
if mapboxStyle.Layers[i].ID == l.MVTName() {
skip = true
break
}
}
// entry for layer already exists. move on
if skip {
continue
}
// build our vector layer details
layer := style.Layer{
ID: l.MVTName(),
Source: req.mapName,
SourceLayer: l.MVTName(),
Layout: &style.LayerLayout{
Visibility: style.LayoutVisible,
},
}
// chose our paint type based on the geometry type
switch l.GeomType.(type) {
case geom.Point, geom.MultiPoint:
layer.Type = style.LayerTypeCircle
layer.Paint = &style.LayerPaint{
CircleRadius: 3,
CircleColor: stringToColorHex(l.MVTName()),
}
case geom.Line, geom.LineString, geom.MultiLineString:
layer.Type = style.LayerTypeLine
layer.Paint = &style.LayerPaint{
LineColor: stringToColorHex(l.MVTName()),
}
case geom.Polygon, geom.MultiPolygon:
layer.Type = style.LayerTypeFill
hexColor := stringToColorHex(l.MVTName())
hex, err := colors.ParseHEX(hexColor)
if err != nil {
log.Errorf("error parsing hex color (%v)", hexColor)
hex, _ = colors.ParseHEX("#fff") // default to white on error
}
rgba := hex.ToRGBA()
// set the opacity to 10%
rgba.A = 0.10
layer.Paint = &style.LayerPaint{
FillColor: rgba.String(),
FillOutlineColor: hexColor,
}
default:
log.Infof("unable to infer geometry type for providerLayerName: %v. style definition not generated", l.ProviderLayerName)
continue
}
// add our layer to our tile layer response
mapboxStyle.Layers = append(mapboxStyle.Layers, layer)
}
// mimetype for protocol buffers
w.Header().Add("Content-Type", "application/json")
// cache control headers (no-cache)
w.Header().Add("Cache-Control", "no-cache, no-store, must-revalidate")
w.Header().Add("Pragma", "no-cache")
w.Header().Add("Expires", "0")
if err = json.NewEncoder(w).Encode(mapboxStyle); err != nil {
log.Errorf("error encoding tileJSON for map (%v)", req.mapName)
}
}
// port of https://stackoverflow.com/questions/3426404/create-a-hexadecimal-colour-based-on-a-string-with-javascript
func stringToColorHex(str string) string {
var hash uint
for i := range []rune(str) {
hash = uint(str[i]) + ((hash << 5) - hash)
}
var color string
for i := 0; i < 3; i++ {
value := (hash >> (uint(i) * 8)) & 0xFF
val := "00" + strconv.FormatUint(uint64(value), 16)
color += val[len(val)-2:]
}
return "#" + color
}