RMO frontend checkpoint

* Create a nwe AddressAndMapLocator which abstracts out the behavior of
   selecting a location
 * Fix the overlay causing render errors on the MapLocator by getting
   rid of the overlay and just using a lock indicator
 * Fix MapLocator zooming in to the wrong place by not framing the
   markers
 * Remove Latlng from platform and just use Location with optional
   accuracy
 * Use nested types with form-encoded POST
 * Fix styles on water report page
This commit is contained in:
Eli Ribble 2026-04-09 17:21:35 +00:00
parent cb9e5146bf
commit 9dccd21cee
No known key found for this signature in database
25 changed files with 828 additions and 598 deletions

View file

@ -39,7 +39,7 @@ func DistrictForLocation(ctx context.Context, lng float64, lat float64) (*models
return nil, errors.New("too many organizations")
}
}
func MatchDistrict(ctx context.Context, longitude, latitude *float64, images []ImageUpload) (*int32, error) {
func MatchDistrict(ctx context.Context, longitude, latitude float64, images []ImageUpload) (*int32, error) {
var err error
var org *models.Organization
for _, image := range images {
@ -58,7 +58,7 @@ func MatchDistrict(ctx context.Context, longitude, latitude *float64, images []I
return &org.ID, nil
}
}
if longitude == nil || latitude == nil {
if longitude == 0 || latitude == 0 {
org, err = DistrictCatchall(ctx)
if err != nil {
return nil, fmt.Errorf("get catchall: %w", err)
@ -66,7 +66,7 @@ func MatchDistrict(ctx context.Context, longitude, latitude *float64, images []I
log.Debug().Int32("id", org.ID).Msg("No location from images, no latlng for the report itself, using catchall")
return &org.ID, nil
}
org, err = DistrictForLocation(ctx, *longitude, *latitude)
org, err = DistrictForLocation(ctx, longitude, latitude)
if err != nil {
log.Warn().Err(err).Msg("Failed to get district for location")
return nil, fmt.Errorf("Failed to get district for location: %w", err)
@ -76,9 +76,9 @@ func MatchDistrict(ctx context.Context, longitude, latitude *float64, images []I
if err != nil {
return nil, fmt.Errorf("get catchall: %w", err)
}
log.Debug().Err(err).Float64("lng", *longitude).Float64("lat", *latitude).Int32("id", org.ID).Msg("No district match by report location, using catchall")
log.Debug().Err(err).Float64("lng", longitude).Float64("lat", latitude).Int32("id", org.ID).Msg("No district match by report location, using catchall")
return &org.ID, nil
}
log.Debug().Err(err).Int32("org_id", org.ID).Float64("lng", *longitude).Float64("lat", *latitude).Msg("Found district match by report location")
log.Debug().Err(err).Int32("org_id", org.ID).Float64("lng", longitude).Float64("lat", latitude).Msg("Found district match by report location")
return &org.ID, nil
}

View file

@ -1,13 +1,7 @@
package platform
import (
"errors"
"fmt"
"github.com/Gleipnir-Technology/nidus-sync/db/enums"
"github.com/Gleipnir-Technology/nidus-sync/h3utils"
"github.com/rs/zerolog/log"
"github.com/uber/h3-go/v4"
)
type LatLng struct {
@ -17,41 +11,3 @@ type LatLng struct {
AccuracyValue float64
AccuracyType enums.PublicreportAccuracytype
}
func (l LatLng) Resolution() uint {
switch l.AccuracyType {
// These accuracy_type strings come from the Mapbox Geocoding API definition and
// are far from scientific
case enums.PublicreportAccuracytypeRooftop:
return 14
case enums.PublicreportAccuracytypeParcel:
return 13
case enums.PublicreportAccuracytypePoint:
return 13
case enums.PublicreportAccuracytypeInterpolated:
return 12
case enums.PublicreportAccuracytypeApproximate:
return 11
case enums.PublicreportAccuracytypeIntersection:
return 10
// This is a special indicator that we got our location from the browser measurements
case enums.PublicreportAccuracytypeBrowser:
return uint(h3utils.MeterAccuracyToH3Resolution(l.AccuracyValue))
default:
log.Warn().Str("accuracy-type", string(l.AccuracyType)).Msg("unrecognized accuracy type, this indicates either a weird client or misbehaving web page. Defaulting to resolution 13")
return 13
}
}
func (l LatLng) H3Cell() (*h3.Cell, error) {
if l.Longitude == nil || l.Latitude == nil {
return nil, errors.New("nil lat/lng")
}
result, err := h3utils.GetCell(*l.Longitude, *l.Latitude, int(l.Resolution()))
return &result, err
}
func (l LatLng) GeometryQuery() (string, error) {
if l.Longitude == nil || l.Latitude == nil {
return "", errors.New("nil lat/lng")
}
return fmt.Sprintf("ST_Point(%f, %f, 4326)", *l.Longitude, *l.Latitude), nil
}

View file

@ -84,8 +84,8 @@ func PublicReportMessageCreate(ctx context.Context, user User, report_id, messag
func PublicReportReporterUpdated(ctx context.Context, org_id int32, report_id string) {
event.Updated(event.TypeRMOReport, org_id, report_id)
}
func ReportNuisanceCreate(ctx context.Context, setter_report models.PublicreportReportSetter, setter_nuisance models.PublicreportNuisanceSetter, latlng LatLng, address Address, images []ImageUpload) (*models.PublicreportReport, error) {
return reportCreate(ctx, setter_report, latlng, address, images, func(ctx context.Context, txn bob.Executor, report_id int32) error {
func ReportNuisanceCreate(ctx context.Context, setter_report models.PublicreportReportSetter, setter_nuisance models.PublicreportNuisanceSetter, location types.Location, address Address, images []ImageUpload) (*models.PublicreportReport, error) {
return reportCreate(ctx, setter_report, location, address, images, func(ctx context.Context, txn bob.Executor, report_id int32) error {
setter_nuisance.ReportID = omit.From(report_id)
_, err := models.PublicreportNuisances.Insert(&setter_nuisance).One(ctx, txn)
if err != nil {
@ -95,8 +95,8 @@ func ReportNuisanceCreate(ctx context.Context, setter_report models.Publicreport
})
}
func ReportWaterCreate(ctx context.Context, setter_report models.PublicreportReportSetter, setter_water models.PublicreportWaterSetter, latlng LatLng, address Address, images []ImageUpload) (*models.PublicreportReport, error) {
return reportCreate(ctx, setter_report, latlng, address, images, func(ctx context.Context, txn bob.Executor, report_id int32) error {
func ReportWaterCreate(ctx context.Context, setter_report models.PublicreportReportSetter, setter_water models.PublicreportWaterSetter, location types.Location, address Address, images []ImageUpload) (*models.PublicreportReport, error) {
return reportCreate(ctx, setter_report, location, address, images, func(ctx context.Context, txn bob.Executor, report_id int32) error {
setter_water.ReportID = omit.From(report_id)
_, err := models.PublicreportWaters.Insert(&setter_water).One(ctx, txn)
if err != nil {
@ -108,7 +108,7 @@ func ReportWaterCreate(ctx context.Context, setter_report models.PublicreportRep
type funcSetReportDetail = func(context.Context, bob.Executor, int32) error
func reportCreate(ctx context.Context, setter_report models.PublicreportReportSetter, latlng LatLng, address Address, images []ImageUpload, detail_setter funcSetReportDetail) (result *models.PublicreportReport, err error) {
func reportCreate(ctx context.Context, setter_report models.PublicreportReportSetter, location types.Location, address Address, images []ImageUpload, detail_setter funcSetReportDetail) (result *models.PublicreportReport, err error) {
txn, err := db.PGInstance.BobDB.BeginTx(ctx, nil)
if err != nil {
return nil, fmt.Errorf("create txn: %w", err)
@ -123,10 +123,10 @@ func reportCreate(ctx context.Context, setter_report models.PublicreportReportSe
// If we've got an locality value it was set by geocoding so we should save it
var a *models.Address
if address.Locality != "" && latlng.Latitude != nil && latlng.Longitude != nil {
if address.Locality != "" && location.Latitude != 0 && location.Longitude != 0 {
a, err = geocode.EnsureAddress(ctx, txn, address, types.Location{
Latitude: *latlng.Latitude,
Longitude: *latlng.Longitude,
Latitude: location.Latitude,
Longitude: location.Longitude,
})
if err != nil {
return nil, fmt.Errorf("Failed to ensure address: %w", err)
@ -138,7 +138,7 @@ func reportCreate(ctx context.Context, setter_report models.PublicreportReportSe
return nil, fmt.Errorf("Failed to save image uploads: %w", err)
}
var organization_id *int32
organization_id, err = MatchDistrict(ctx, latlng.Longitude, latlng.Latitude, images)
organization_id, err = MatchDistrict(ctx, location.Longitude, location.Latitude, images)
if err != nil {
log.Warn().Err(err).Msg("Failed to match district")
}
@ -153,9 +153,9 @@ func reportCreate(ctx context.Context, setter_report models.PublicreportReportSe
if err != nil {
return nil, fmt.Errorf("Failed to create report database record: %w", err)
}
if latlng.Latitude != nil && latlng.Longitude != nil {
h3cell, _ := latlng.H3Cell()
geom_query, _ := latlng.GeometryQuery()
if location.Latitude != 0 && location.Longitude != 0 {
h3cell, _ := location.H3Cell()
geom_query, _ := location.GeometryQuery()
_, err = psql.Update(
um.Table("publicreport.report"),
um.SetCol("h3cell").ToArg(h3cell),

View file

@ -8,11 +8,11 @@ import (
type Address struct {
Country string `db:"country" json:"country"`
GID string `db:"gid" json:"gid"`
GID string `db:"gid" json:"gid" schema:"gid"`
Locality string `db:"locality" json:"locality"`
Number string `db:"number" json:"number"`
PostalCode string `db:"postal_code" json:"postal_code"`
Raw string `db:"raw" json:"raw"`
Raw string `db:"raw" json:"raw" schema:"raw"`
Region string `db:"region" json:"region"`
Street string `db:"street" json:"street"`
Unit string `db:"unit" json:"unit"`

View file

@ -2,13 +2,33 @@ package types
import (
"fmt"
"github.com/Gleipnir-Technology/nidus-sync/h3utils"
//"github.com/rs/zerolog/log"
"github.com/uber/h3-go/v4"
)
type Location struct {
Latitude float64 `db:"latitude" json:"latitude"`
Longitude float64 `db:"longitude" json:"longitude"`
Accuracy *float32 `db:"accuracy" json:"accuracy" schema:"accuracy"`
Latitude float64 `db:"latitude" json:"latitude" schema:"latitude"`
Longitude float64 `db:"longitude" json:"longitude" schema:"longitude"`
}
func (l Location) String() string {
return fmt.Sprintf("%f %f", l.Longitude, l.Latitude)
}
func (l Location) Resolution() uint {
if l.Accuracy != nil {
return uint(h3utils.MeterAccuracyToH3Resolution(float64(*l.Accuracy)))
} else {
return uint(0)
}
}
func (l Location) H3Cell() (*h3.Cell, error) {
result, err := h3utils.GetCell(l.Longitude, l.Latitude, int(l.Resolution()))
return &result, err
}
func (l Location) GeometryQuery() (string, error) {
return fmt.Sprintf("ST_Point(%f, %f, 4326)", l.Longitude, l.Latitude), nil
}