Add geometry info to h3 aggregation table
This makes it possible to use Tegola to show vector tiles.
This commit is contained in:
parent
6f6af23578
commit
a14249710d
6 changed files with 101 additions and 30 deletions
11
database.go
11
database.go
|
|
@ -177,11 +177,16 @@ func updateSummaryTables(ctx context.Context, org *models.Organization) {
|
||||||
cellToCount[cell] = cellToCount[cell] + 1
|
cellToCount[cell] = cellToCount[cell] + 1
|
||||||
}
|
}
|
||||||
var to_insert []bob.Mod[*dialect.InsertQuery] = make([]bob.Mod[*dialect.InsertQuery], 0)
|
var to_insert []bob.Mod[*dialect.InsertQuery] = make([]bob.Mod[*dialect.InsertQuery], 0)
|
||||||
to_insert = append(to_insert, im.Into("h3_aggregation", "cell", "resolution", "count_", "type_", "organization_id"))
|
to_insert = append(to_insert, im.Into("h3_aggregation", "cell", "resolution", "count_", "type_", "organization_id", "geometry"))
|
||||||
for cell, count := range cellToCount {
|
for cell, count := range cellToCount {
|
||||||
to_insert = append(to_insert, im.Values(psql.Arg(cell.String(), i, count, enums.H3aggregationtypeServicerequest, org.ID)))
|
polygon, err := cellToPostgisGeometry(cell)
|
||||||
|
if err != nil {
|
||||||
|
log.Error().Err(err).Msg("Failed to get PostGIS geometry")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// log.Info().Str("polygon", polygon).Msg("Going to insert")
|
||||||
|
to_insert = append(to_insert, im.Values(psql.Arg(cell.String(), i, count, enums.H3aggregationtypeServicerequest, org.ID), psql.F("st_geomfromtext", psql.S(polygon), 4326)))
|
||||||
}
|
}
|
||||||
//to_insert = append(to_insert, im.OnConflict("h3_aggregation_cell_organization_id_type__key").DoUpdate(
|
|
||||||
to_insert = append(to_insert, im.OnConflict("cell, organization_id, type_").DoUpdate(
|
to_insert = append(to_insert, im.OnConflict("cell, organization_id, type_").DoUpdate(
|
||||||
im.SetCol("count_").To(psql.Raw("EXCLUDED.count_")),
|
im.SetCol("count_").To(psql.Raw("EXCLUDED.count_")),
|
||||||
))
|
))
|
||||||
|
|
|
||||||
30
h3.go
30
h3.go
|
|
@ -2,6 +2,7 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/Gleipnir-Technology/go-geojson2h3"
|
"github.com/Gleipnir-Technology/go-geojson2h3"
|
||||||
"github.com/tidwall/geojson"
|
"github.com/tidwall/geojson"
|
||||||
|
|
@ -35,6 +36,15 @@ func h3ToGeoJSON(indexes []h3.Cell) (string, error) {
|
||||||
}
|
}
|
||||||
return featureCollection.JSON(), nil
|
return featureCollection.JSON(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func sampleGeoJSON() (string, error) {
|
||||||
|
indexes := h3Indexes()
|
||||||
|
featureCollection, err := geojson2h3.ToFeatureCollection(indexes)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("Failed to get feature collection: %w", err)
|
||||||
|
}
|
||||||
|
return featureCollection.JSON(), nil
|
||||||
|
}
|
||||||
func main2() {
|
func main2() {
|
||||||
resolution := 9
|
resolution := 9
|
||||||
object, err := geojson.Parse(`{"type":"FeatureCollection","features":[{"type":"Feature","properties":{"shape":"Polygon","name":"Unnamed Layer","category":"default"},"geometry":{"type":"Polygon","coordinates":[[[-73.901303,40.756892],[-73.893924,40.743755],[-73.871476,40.756278],[-73.863378,40.764175],[-73.871444,40.768467],[-73.879852,40.760014],[-73.885515,40.764045],[-73.891522,40.761054],[-73.901303,40.756892]]]},"id":"a6ca1b7e-9ddf-4425-ad07-8a895f7d6ccf"}]}`, nil)
|
object, err := geojson.Parse(`{"type":"FeatureCollection","features":[{"type":"Feature","properties":{"shape":"Polygon","name":"Unnamed Layer","category":"default"},"geometry":{"type":"Polygon","coordinates":[[[-73.901303,40.756892],[-73.893924,40.743755],[-73.871476,40.756278],[-73.863378,40.764175],[-73.871444,40.768467],[-73.879852,40.760014],[-73.885515,40.764045],[-73.891522,40.761054],[-73.901303,40.756892]]]},"id":"a6ca1b7e-9ddf-4425-ad07-8a895f7d6ccf"}]}`, nil)
|
||||||
|
|
@ -79,3 +89,23 @@ func getCell(x, y float64, resolution int) (h3.Cell, error) {
|
||||||
latLng := h3.NewLatLng(y, x)
|
latLng := h3.NewLatLng(y, x)
|
||||||
return h3.LatLngToCell(latLng, resolution)
|
return h3.LatLngToCell(latLng, resolution)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func cellToPostgisGeometry(c h3.Cell) (string, error) {
|
||||||
|
boundary, err := h3.CellToBoundary(c)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("Failed to get cell boundary: %w", err)
|
||||||
|
}
|
||||||
|
var sb strings.Builder
|
||||||
|
|
||||||
|
for i, p := range boundary {
|
||||||
|
if i > 0 {
|
||||||
|
sb.WriteString(",")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(&sb, "%g %g", p.Lng, p.Lat)
|
||||||
|
}
|
||||||
|
// add the first point on to the end to close the polygon
|
||||||
|
sb.WriteString(",")
|
||||||
|
fmt.Fprintf(&sb, "%g %g", boundary[0].Lng, boundary[0].Lat)
|
||||||
|
|
||||||
|
return fmt.Sprintf("POLYGON((%s))", sb.String()), nil
|
||||||
|
}
|
||||||
|
|
|
||||||
2
html.go
2
html.go
|
|
@ -166,7 +166,7 @@ func extractInitials(name string) string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func htmlDashboard(ctx context.Context, w http.ResponseWriter, user *models.User) {
|
func htmlDashboard(ctx context.Context, w http.ResponseWriter, user *models.User) {
|
||||||
geo, err := h3ToGeoJSON(h3Indexes())
|
geo, err := sampleGeoJSON()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
respondError(w, "Failed to get geo", err, http.StatusInternalServerError)
|
respondError(w, "Failed to get geo", err, http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
|
|
|
||||||
1
main.go
1
main.go
|
|
@ -111,6 +111,7 @@ func main() {
|
||||||
|
|
||||||
// Authenticated endpoints
|
// Authenticated endpoints
|
||||||
r.Method("GET", "/settings", NewEnsureAuth(getSettings))
|
r.Method("GET", "/settings", NewEnsureAuth(getSettings))
|
||||||
|
r.Method("GET", "/vector-tiles/{org_id}/{tileset_id}/{zoom}/{x}/{y}.{format}", NewEnsureAuth(getVectorTiles))
|
||||||
|
|
||||||
localFS := http.Dir("./static")
|
localFS := http.Dir("./static")
|
||||||
FileServer(r, "/static", localFS, embeddedStaticFS, "static")
|
FileServer(r, "/static", localFS, embeddedStaticFS, "static")
|
||||||
|
|
|
||||||
|
|
@ -8,10 +8,11 @@ CREATE TYPE H3AggregationType AS ENUM (
|
||||||
CREATE TABLE h3_aggregation (
|
CREATE TABLE h3_aggregation (
|
||||||
id SERIAL PRIMARY KEY,
|
id SERIAL PRIMARY KEY,
|
||||||
cell h3index NOT NULL,
|
cell h3index NOT NULL,
|
||||||
resolution INT NOT NULL,
|
|
||||||
count_ INTEGER NOT NULL,
|
count_ INTEGER NOT NULL,
|
||||||
type_ H3AggregationType NOT NULL,
|
geometry public.geometry(Polygon,4326),
|
||||||
organization_id INTEGER REFERENCES organization (id) NOT NULL,
|
organization_id INTEGER REFERENCES organization (id) NOT NULL,
|
||||||
|
resolution INT NOT NULL,
|
||||||
|
type_ H3AggregationType NOT NULL,
|
||||||
UNIQUE(cell, organization_id, type_));
|
UNIQUE(cell, organization_id, type_));
|
||||||
|
|
||||||
-- +goose Down
|
-- +goose Down
|
||||||
|
|
|
||||||
|
|
@ -11,36 +11,70 @@ function onLoad() {
|
||||||
mapboxgl.accessToken = {{ .MapboxToken }};
|
mapboxgl.accessToken = {{ .MapboxToken }};
|
||||||
const map = new mapboxgl.Map({
|
const map = new mapboxgl.Map({
|
||||||
container: 'map', // container ID
|
container: 'map', // container ID
|
||||||
style: 'mapbox://styles/mapbox/streets-v12', // style URL
|
style: 'mapbox://styles/mapbox/standard', // style URL
|
||||||
center: [-74.5, 40], // starting position [lng, lat]
|
center: [-119.3, 36.327], // starting position [lng, lat]
|
||||||
zoom: 9, // starting zoom
|
//center: [7.01, 50.74],
|
||||||
|
zoom: 9 // starting zoom
|
||||||
});
|
});
|
||||||
map.on("load", function() {
|
map.on("load", function() {
|
||||||
console.log("Map post-load...");
|
console.log("Map post-load...");
|
||||||
const sourceId = 'h3-hexes';
|
map.addSource('tegola-bonn', {
|
||||||
const layerId = 'h3-hexes-layer';
|
'type': 'vector',
|
||||||
let source = map.getSource(sourceId);
|
'tiles': [
|
||||||
|
//'https://tiles.mapillary.com/maps/vtp/mly1_public/2/{z}/{x}/{y}?access_token=MLY|4142433049200173|72206abe5035850d6743b23a49c41333'
|
||||||
if (!source) {
|
'https://tegola.nidus.cloud/maps/bonn/{z}/{x}/{y}'
|
||||||
map.addSource(sourceId, {
|
]
|
||||||
type: 'geojson',
|
//'minzoom': 6,
|
||||||
data: geojson
|
//'maxzoom': 14
|
||||||
|
});
|
||||||
|
map.addSource('tegola-nidus', {
|
||||||
|
'type': 'vector',
|
||||||
|
'tiles': [
|
||||||
|
//'https://tiles.mapillary.com/maps/vtp/mly1_public/2/{z}/{x}/{y}?access_token=MLY|4142433049200173|72206abe5035850d6743b23a49c41333'
|
||||||
|
'https://tegola.nidus.cloud/maps/nidus/{z}/{x}/{y}'
|
||||||
|
]
|
||||||
|
//'minzoom': 6,
|
||||||
|
//'maxzoom': 14
|
||||||
});
|
});
|
||||||
map.addLayer({
|
map.addLayer({
|
||||||
id: layerId,
|
'id': 'bonn', // Layer ID
|
||||||
source: sourceId,
|
'type': 'fill',
|
||||||
type: 'fill',
|
'source': 'tegola-bonn', // ID of the tile source created above
|
||||||
interactive: false,
|
'source-layer': 'lakes',
|
||||||
paint: {
|
'layout': {
|
||||||
'fill-color': '#F00000',
|
'line-cap': 'round',
|
||||||
'fill-opacity': 0.3
|
'line-join': 'round'
|
||||||
|
},
|
||||||
|
'paint': {
|
||||||
|
'fill-opacity': 0.1,
|
||||||
|
'line-opacity': 0.6,
|
||||||
|
'line-color': 'rgb(53, 175, 109)',
|
||||||
|
'line-width': 2
|
||||||
}
|
}
|
||||||
|
//slot: 'middle' // middle slot in Mapbox Standard style
|
||||||
});
|
});
|
||||||
source = map.getSource(sourceId);
|
map.addLayer({
|
||||||
|
'id': 'nidus', // Layer ID
|
||||||
|
'type': 'fill',
|
||||||
|
'source': 'tegola-nidus', // ID of the tile source created above
|
||||||
|
'source-layer': 'h3_aggregation',
|
||||||
|
'layout': {
|
||||||
|
'line-cap': 'round',
|
||||||
|
'line-join': 'round'
|
||||||
|
},
|
||||||
|
'paint': {
|
||||||
|
'fill-opacity': 0.3,
|
||||||
|
'line-opacity': 0.6,
|
||||||
|
'line-color': 'rgb(53, 175, 109)',
|
||||||
|
'line-width': 2
|
||||||
}
|
}
|
||||||
source.setData(geojson);
|
//slot: 'middle' // middle slot in Mapbox Standard style
|
||||||
|
});
|
||||||
|
|
||||||
console.log("Map post-load done.");
|
console.log("Map post-load done.");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
map.addControl(new mapboxgl.NavigationControl());
|
||||||
console.log("Map init done.");
|
console.log("Map init done.");
|
||||||
}
|
}
|
||||||
window.addEventListener("load", onLoad);
|
window.addEventListener("load", onLoad);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue