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
|
||||
}
|
||||
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 {
|
||||
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(
|
||||
im.SetCol("count_").To(psql.Raw("EXCLUDED.count_")),
|
||||
))
|
||||
|
|
|
|||
30
h3.go
30
h3.go
|
|
@ -2,6 +2,7 @@ package main
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/Gleipnir-Technology/go-geojson2h3"
|
||||
"github.com/tidwall/geojson"
|
||||
|
|
@ -35,6 +36,15 @@ func h3ToGeoJSON(indexes []h3.Cell) (string, error) {
|
|||
}
|
||||
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() {
|
||||
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)
|
||||
|
|
@ -79,3 +89,23 @@ func getCell(x, y float64, resolution int) (h3.Cell, error) {
|
|||
latLng := h3.NewLatLng(y, x)
|
||||
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) {
|
||||
geo, err := h3ToGeoJSON(h3Indexes())
|
||||
geo, err := sampleGeoJSON()
|
||||
if err != nil {
|
||||
respondError(w, "Failed to get geo", err, http.StatusInternalServerError)
|
||||
return
|
||||
|
|
|
|||
1
main.go
1
main.go
|
|
@ -111,6 +111,7 @@ func main() {
|
|||
|
||||
// Authenticated endpoints
|
||||
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")
|
||||
FileServer(r, "/static", localFS, embeddedStaticFS, "static")
|
||||
|
|
|
|||
|
|
@ -8,10 +8,11 @@ CREATE TYPE H3AggregationType AS ENUM (
|
|||
CREATE TABLE h3_aggregation (
|
||||
id SERIAL PRIMARY KEY,
|
||||
cell h3index NOT NULL,
|
||||
resolution INT NOT NULL,
|
||||
count_ INTEGER NOT NULL,
|
||||
type_ H3AggregationType NOT NULL,
|
||||
geometry public.geometry(Polygon,4326),
|
||||
organization_id INTEGER REFERENCES organization (id) NOT NULL,
|
||||
resolution INT NOT NULL,
|
||||
type_ H3AggregationType NOT NULL,
|
||||
UNIQUE(cell, organization_id, type_));
|
||||
|
||||
-- +goose Down
|
||||
|
|
|
|||
|
|
@ -11,36 +11,70 @@ function onLoad() {
|
|||
mapboxgl.accessToken = {{ .MapboxToken }};
|
||||
const map = new mapboxgl.Map({
|
||||
container: 'map', // container ID
|
||||
style: 'mapbox://styles/mapbox/streets-v12', // style URL
|
||||
center: [-74.5, 40], // starting position [lng, lat]
|
||||
zoom: 9, // starting zoom
|
||||
style: 'mapbox://styles/mapbox/standard', // style URL
|
||||
center: [-119.3, 36.327], // starting position [lng, lat]
|
||||
//center: [7.01, 50.74],
|
||||
zoom: 9 // starting zoom
|
||||
});
|
||||
map.on("load", function() {
|
||||
console.log("Map post-load...");
|
||||
const sourceId = 'h3-hexes';
|
||||
const layerId = 'h3-hexes-layer';
|
||||
let source = map.getSource(sourceId);
|
||||
map.addSource('tegola-bonn', {
|
||||
'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/bonn/{z}/{x}/{y}'
|
||||
]
|
||||
//'minzoom': 6,
|
||||
//'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({
|
||||
'id': 'bonn', // Layer ID
|
||||
'type': 'fill',
|
||||
'source': 'tegola-bonn', // ID of the tile source created above
|
||||
'source-layer': 'lakes',
|
||||
'layout': {
|
||||
'line-cap': 'round',
|
||||
'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
|
||||
});
|
||||
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
|
||||
}
|
||||
//slot: 'middle' // middle slot in Mapbox Standard style
|
||||
});
|
||||
|
||||
if (!source) {
|
||||
map.addSource(sourceId, {
|
||||
type: 'geojson',
|
||||
data: geojson
|
||||
});
|
||||
map.addLayer({
|
||||
id: layerId,
|
||||
source: sourceId,
|
||||
type: 'fill',
|
||||
interactive: false,
|
||||
paint: {
|
||||
'fill-color': '#F00000',
|
||||
'fill-opacity': 0.3
|
||||
}
|
||||
});
|
||||
source = map.getSource(sourceId);
|
||||
}
|
||||
source.setData(geojson);
|
||||
console.log("Map post-load done.");
|
||||
});
|
||||
|
||||
map.addControl(new mapboxgl.NavigationControl());
|
||||
console.log("Map init done.");
|
||||
}
|
||||
window.addEventListener("load", onLoad);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue