From a1cc2dbaffe2c602cdb3de6588f775f9e3472ddd Mon Sep 17 00:00:00 2001
From: Eli Ribble
Date: Mon, 16 Feb 2026 15:03:26 +0000
Subject: [PATCH] Add district setting page and display of district boundary
---
db/dbinfo/import.district.bob.go | 68 ++++---
db/factory/bobfactory_main.bob.go | 2 +
db/factory/import.district.bob.go | 162 ++++++++++++---
db/models/import.district.bob.go | 246 ++++++++++++-----------
html/static/js/map-district.js | 135 +++++++++++++
html/template/sync/setting-district.html | 136 +++++++++++++
html/template/sync/settings.html | 2 +-
sync/routes.go | 1 +
sync/setting.go | 60 +++++-
sync/url.go | 2 +
tools/drop-and-recreate.sql | 2 +
11 files changed, 648 insertions(+), 168 deletions(-)
create mode 100644 html/static/js/map-district.js
create mode 100644 html/template/sync/setting-district.html
diff --git a/db/dbinfo/import.district.bob.go b/db/dbinfo/import.district.bob.go
index cefc73ed..27415c50 100644
--- a/db/dbinfo/import.district.bob.go
+++ b/db/dbinfo/import.district.bob.go
@@ -222,6 +222,24 @@ var ImportDistricts = Table[
Generated: true,
AutoIncr: false,
},
+ Centroid4326: column{
+ Name: "centroid_4326",
+ DBType: "geometry",
+ Default: "GENERATED",
+ Comment: "",
+ Nullable: true,
+ Generated: true,
+ AutoIncr: false,
+ },
+ Extent4326: column{
+ Name: "extent_4326",
+ DBType: "geometry",
+ Default: "GENERATED",
+ Comment: "",
+ Nullable: true,
+ Generated: true,
+ AutoIncr: false,
+ },
},
Indexes: importDistrictIndexes{
DistrictPkey: index{
@@ -269,34 +287,36 @@ var ImportDistricts = Table[
}
type importDistrictColumns struct {
- Gid column
- ID column
- Website column
- Contact column
- Address column
- Regionid column
- PostalCod column
- Phone1 column
- Fax1 column
- Agency column
- Code1 column
- City1 column
- ShapeLeng column
- Address2 column
- GeneralMG column
- City2 column
- PostalC1 column
- Fax2 column
- Phone2 column
- ShapeLe1 column
- ShapeArea column
- Geom column
- Geom4326 column
+ Gid column
+ ID column
+ Website column
+ Contact column
+ Address column
+ Regionid column
+ PostalCod column
+ Phone1 column
+ Fax1 column
+ Agency column
+ Code1 column
+ City1 column
+ ShapeLeng column
+ Address2 column
+ GeneralMG column
+ City2 column
+ PostalC1 column
+ Fax2 column
+ Phone2 column
+ ShapeLe1 column
+ ShapeArea column
+ Geom column
+ Geom4326 column
+ Centroid4326 column
+ Extent4326 column
}
func (c importDistrictColumns) AsSlice() []column {
return []column{
- c.Gid, c.ID, c.Website, c.Contact, c.Address, c.Regionid, c.PostalCod, c.Phone1, c.Fax1, c.Agency, c.Code1, c.City1, c.ShapeLeng, c.Address2, c.GeneralMG, c.City2, c.PostalC1, c.Fax2, c.Phone2, c.ShapeLe1, c.ShapeArea, c.Geom, c.Geom4326,
+ c.Gid, c.ID, c.Website, c.Contact, c.Address, c.Regionid, c.PostalCod, c.Phone1, c.Fax1, c.Agency, c.Code1, c.City1, c.ShapeLeng, c.Address2, c.GeneralMG, c.City2, c.PostalC1, c.Fax2, c.Phone2, c.ShapeLe1, c.ShapeArea, c.Geom, c.Geom4326, c.Centroid4326, c.Extent4326,
}
}
diff --git a/db/factory/bobfactory_main.bob.go b/db/factory/bobfactory_main.bob.go
index de6e0aea..767a1715 100644
--- a/db/factory/bobfactory_main.bob.go
+++ b/db/factory/bobfactory_main.bob.go
@@ -2622,6 +2622,8 @@ func (f *Factory) FromExistingImportDistrict(m *models.ImportDistrict) *ImportDi
o.ShapeArea = func() null.Val[decimal.Decimal] { return m.ShapeArea }
o.Geom = func() null.Val[string] { return m.Geom }
o.Geom4326 = func() null.Val[string] { return m.Geom4326 }
+ o.Centroid4326 = func() null.Val[string] { return m.Centroid4326 }
+ o.Extent4326 = func() null.Val[string] { return m.Extent4326 }
ctx := context.Background()
if m.R.ImportDistrictGidOrganization != nil {
diff --git a/db/factory/import.district.bob.go b/db/factory/import.district.bob.go
index 3cadd923..73952b69 100644
--- a/db/factory/import.district.bob.go
+++ b/db/factory/import.district.bob.go
@@ -37,29 +37,31 @@ func (mods ImportDistrictModSlice) Apply(ctx context.Context, n *ImportDistrictT
// ImportDistrictTemplate is an object representing the database table.
// all columns are optional and should be set by mods
type ImportDistrictTemplate struct {
- Gid func() int32
- ID func() null.Val[decimal.Decimal]
- Website func() null.Val[string]
- Contact func() null.Val[string]
- Address func() null.Val[string]
- Regionid func() null.Val[decimal.Decimal]
- PostalCod func() null.Val[decimal.Decimal]
- Phone1 func() null.Val[string]
- Fax1 func() null.Val[string]
- Agency func() null.Val[string]
- Code1 func() null.Val[string]
- City1 func() null.Val[string]
- ShapeLeng func() null.Val[decimal.Decimal]
- Address2 func() null.Val[string]
- GeneralMG func() null.Val[string]
- City2 func() null.Val[string]
- PostalC1 func() null.Val[decimal.Decimal]
- Fax2 func() null.Val[string]
- Phone2 func() null.Val[string]
- ShapeLe1 func() null.Val[decimal.Decimal]
- ShapeArea func() null.Val[decimal.Decimal]
- Geom func() null.Val[string]
- Geom4326 func() null.Val[string]
+ Gid func() int32
+ ID func() null.Val[decimal.Decimal]
+ Website func() null.Val[string]
+ Contact func() null.Val[string]
+ Address func() null.Val[string]
+ Regionid func() null.Val[decimal.Decimal]
+ PostalCod func() null.Val[decimal.Decimal]
+ Phone1 func() null.Val[string]
+ Fax1 func() null.Val[string]
+ Agency func() null.Val[string]
+ Code1 func() null.Val[string]
+ City1 func() null.Val[string]
+ ShapeLeng func() null.Val[decimal.Decimal]
+ Address2 func() null.Val[string]
+ GeneralMG func() null.Val[string]
+ City2 func() null.Val[string]
+ PostalC1 func() null.Val[decimal.Decimal]
+ Fax2 func() null.Val[string]
+ Phone2 func() null.Val[string]
+ ShapeLe1 func() null.Val[decimal.Decimal]
+ ShapeArea func() null.Val[decimal.Decimal]
+ Geom func() null.Val[string]
+ Geom4326 func() null.Val[string]
+ Centroid4326 func() null.Val[string]
+ Extent4326 func() null.Val[string]
r importDistrictR
f *Factory
@@ -277,6 +279,12 @@ func (o ImportDistrictTemplate) Build() *models.ImportDistrict {
if o.Geom4326 != nil {
m.Geom4326 = o.Geom4326()
}
+ if o.Centroid4326 != nil {
+ m.Centroid4326 = o.Centroid4326()
+ }
+ if o.Extent4326 != nil {
+ m.Extent4326 = o.Extent4326()
+ }
o.setModelRels(m)
@@ -439,6 +447,8 @@ func (m importDistrictMods) RandomizeAllColumns(f *faker.Faker) ImportDistrictMo
ImportDistrictMods.RandomShapeArea(f),
ImportDistrictMods.RandomGeom(f),
ImportDistrictMods.RandomGeom4326(f),
+ ImportDistrictMods.RandomCentroid4326(f),
+ ImportDistrictMods.RandomExtent4326(f),
}
}
@@ -1639,6 +1649,112 @@ func (m importDistrictMods) RandomGeom4326NotNull(f *faker.Faker) ImportDistrict
})
}
+// Set the model columns to this value
+func (m importDistrictMods) Centroid4326(val null.Val[string]) ImportDistrictMod {
+ return ImportDistrictModFunc(func(_ context.Context, o *ImportDistrictTemplate) {
+ o.Centroid4326 = func() null.Val[string] { return val }
+ })
+}
+
+// Set the Column from the function
+func (m importDistrictMods) Centroid4326Func(f func() null.Val[string]) ImportDistrictMod {
+ return ImportDistrictModFunc(func(_ context.Context, o *ImportDistrictTemplate) {
+ o.Centroid4326 = f
+ })
+}
+
+// Clear any values for the column
+func (m importDistrictMods) UnsetCentroid4326() ImportDistrictMod {
+ return ImportDistrictModFunc(func(_ context.Context, o *ImportDistrictTemplate) {
+ o.Centroid4326 = nil
+ })
+}
+
+// Generates a random value for the column using the given faker
+// if faker is nil, a default faker is used
+// The generated value is sometimes null
+func (m importDistrictMods) RandomCentroid4326(f *faker.Faker) ImportDistrictMod {
+ return ImportDistrictModFunc(func(_ context.Context, o *ImportDistrictTemplate) {
+ o.Centroid4326 = func() null.Val[string] {
+ if f == nil {
+ f = &defaultFaker
+ }
+
+ val := random_string(f)
+ return null.From(val)
+ }
+ })
+}
+
+// Generates a random value for the column using the given faker
+// if faker is nil, a default faker is used
+// The generated value is never null
+func (m importDistrictMods) RandomCentroid4326NotNull(f *faker.Faker) ImportDistrictMod {
+ return ImportDistrictModFunc(func(_ context.Context, o *ImportDistrictTemplate) {
+ o.Centroid4326 = func() null.Val[string] {
+ if f == nil {
+ f = &defaultFaker
+ }
+
+ val := random_string(f)
+ return null.From(val)
+ }
+ })
+}
+
+// Set the model columns to this value
+func (m importDistrictMods) Extent4326(val null.Val[string]) ImportDistrictMod {
+ return ImportDistrictModFunc(func(_ context.Context, o *ImportDistrictTemplate) {
+ o.Extent4326 = func() null.Val[string] { return val }
+ })
+}
+
+// Set the Column from the function
+func (m importDistrictMods) Extent4326Func(f func() null.Val[string]) ImportDistrictMod {
+ return ImportDistrictModFunc(func(_ context.Context, o *ImportDistrictTemplate) {
+ o.Extent4326 = f
+ })
+}
+
+// Clear any values for the column
+func (m importDistrictMods) UnsetExtent4326() ImportDistrictMod {
+ return ImportDistrictModFunc(func(_ context.Context, o *ImportDistrictTemplate) {
+ o.Extent4326 = nil
+ })
+}
+
+// Generates a random value for the column using the given faker
+// if faker is nil, a default faker is used
+// The generated value is sometimes null
+func (m importDistrictMods) RandomExtent4326(f *faker.Faker) ImportDistrictMod {
+ return ImportDistrictModFunc(func(_ context.Context, o *ImportDistrictTemplate) {
+ o.Extent4326 = func() null.Val[string] {
+ if f == nil {
+ f = &defaultFaker
+ }
+
+ val := random_string(f)
+ return null.From(val)
+ }
+ })
+}
+
+// Generates a random value for the column using the given faker
+// if faker is nil, a default faker is used
+// The generated value is never null
+func (m importDistrictMods) RandomExtent4326NotNull(f *faker.Faker) ImportDistrictMod {
+ return ImportDistrictModFunc(func(_ context.Context, o *ImportDistrictTemplate) {
+ o.Extent4326 = func() null.Val[string] {
+ if f == nil {
+ f = &defaultFaker
+ }
+
+ val := random_string(f)
+ return null.From(val)
+ }
+ })
+}
+
func (m importDistrictMods) WithParentsCascading() ImportDistrictMod {
return ImportDistrictModFunc(func(ctx context.Context, o *ImportDistrictTemplate) {
if isDone, _ := importDistrictWithParentsCascadingCtx.Value(ctx); isDone {
diff --git a/db/models/import.district.bob.go b/db/models/import.district.bob.go
index 6523d151..725b78e4 100644
--- a/db/models/import.district.bob.go
+++ b/db/models/import.district.bob.go
@@ -26,29 +26,31 @@ import (
// ImportDistrict is an object representing the database table.
type ImportDistrict struct {
- Gid int32 `db:"gid,pk" `
- ID null.Val[decimal.Decimal] `db:"id" `
- Website null.Val[string] `db:"website" `
- Contact null.Val[string] `db:"contact" `
- Address null.Val[string] `db:"address" `
- Regionid null.Val[decimal.Decimal] `db:"regionid" `
- PostalCod null.Val[decimal.Decimal] `db:"postal_cod" `
- Phone1 null.Val[string] `db:"phone1" `
- Fax1 null.Val[string] `db:"fax1" `
- Agency null.Val[string] `db:"agency" `
- Code1 null.Val[string] `db:"code1" `
- City1 null.Val[string] `db:"city1" `
- ShapeLeng null.Val[decimal.Decimal] `db:"shape_leng" `
- Address2 null.Val[string] `db:"address2" `
- GeneralMG null.Val[string] `db:"general_mg" `
- City2 null.Val[string] `db:"city2" `
- PostalC1 null.Val[decimal.Decimal] `db:"postal_c_1" `
- Fax2 null.Val[string] `db:"fax2" `
- Phone2 null.Val[string] `db:"phone2" `
- ShapeLe1 null.Val[decimal.Decimal] `db:"shape_le_1" `
- ShapeArea null.Val[decimal.Decimal] `db:"shape_area" `
- Geom null.Val[string] `db:"geom" `
- Geom4326 null.Val[string] `db:"geom_4326,generated" `
+ Gid int32 `db:"gid,pk" `
+ ID null.Val[decimal.Decimal] `db:"id" `
+ Website null.Val[string] `db:"website" `
+ Contact null.Val[string] `db:"contact" `
+ Address null.Val[string] `db:"address" `
+ Regionid null.Val[decimal.Decimal] `db:"regionid" `
+ PostalCod null.Val[decimal.Decimal] `db:"postal_cod" `
+ Phone1 null.Val[string] `db:"phone1" `
+ Fax1 null.Val[string] `db:"fax1" `
+ Agency null.Val[string] `db:"agency" `
+ Code1 null.Val[string] `db:"code1" `
+ City1 null.Val[string] `db:"city1" `
+ ShapeLeng null.Val[decimal.Decimal] `db:"shape_leng" `
+ Address2 null.Val[string] `db:"address2" `
+ GeneralMG null.Val[string] `db:"general_mg" `
+ City2 null.Val[string] `db:"city2" `
+ PostalC1 null.Val[decimal.Decimal] `db:"postal_c_1" `
+ Fax2 null.Val[string] `db:"fax2" `
+ Phone2 null.Val[string] `db:"phone2" `
+ ShapeLe1 null.Val[decimal.Decimal] `db:"shape_le_1" `
+ ShapeArea null.Val[decimal.Decimal] `db:"shape_area" `
+ Geom null.Val[string] `db:"geom" `
+ Geom4326 null.Val[string] `db:"geom_4326,generated" `
+ Centroid4326 null.Val[string] `db:"centroid_4326,generated" `
+ Extent4326 null.Val[string] `db:"extent_4326,generated" `
R importDistrictR `db:"-" `
}
@@ -71,61 +73,65 @@ type importDistrictR struct {
func buildImportDistrictColumns(alias string) importDistrictColumns {
return importDistrictColumns{
ColumnsExpr: expr.NewColumnsExpr(
- "gid", "id", "website", "contact", "address", "regionid", "postal_cod", "phone1", "fax1", "agency", "code1", "city1", "shape_leng", "address2", "general_mg", "city2", "postal_c_1", "fax2", "phone2", "shape_le_1", "shape_area", "geom", "geom_4326",
+ "gid", "id", "website", "contact", "address", "regionid", "postal_cod", "phone1", "fax1", "agency", "code1", "city1", "shape_leng", "address2", "general_mg", "city2", "postal_c_1", "fax2", "phone2", "shape_le_1", "shape_area", "geom", "geom_4326", "centroid_4326", "extent_4326",
).WithParent("import.district"),
- tableAlias: alias,
- Gid: psql.Quote(alias, "gid"),
- ID: psql.Quote(alias, "id"),
- Website: psql.Quote(alias, "website"),
- Contact: psql.Quote(alias, "contact"),
- Address: psql.Quote(alias, "address"),
- Regionid: psql.Quote(alias, "regionid"),
- PostalCod: psql.Quote(alias, "postal_cod"),
- Phone1: psql.Quote(alias, "phone1"),
- Fax1: psql.Quote(alias, "fax1"),
- Agency: psql.Quote(alias, "agency"),
- Code1: psql.Quote(alias, "code1"),
- City1: psql.Quote(alias, "city1"),
- ShapeLeng: psql.Quote(alias, "shape_leng"),
- Address2: psql.Quote(alias, "address2"),
- GeneralMG: psql.Quote(alias, "general_mg"),
- City2: psql.Quote(alias, "city2"),
- PostalC1: psql.Quote(alias, "postal_c_1"),
- Fax2: psql.Quote(alias, "fax2"),
- Phone2: psql.Quote(alias, "phone2"),
- ShapeLe1: psql.Quote(alias, "shape_le_1"),
- ShapeArea: psql.Quote(alias, "shape_area"),
- Geom: psql.Quote(alias, "geom"),
- Geom4326: psql.Quote(alias, "geom_4326"),
+ tableAlias: alias,
+ Gid: psql.Quote(alias, "gid"),
+ ID: psql.Quote(alias, "id"),
+ Website: psql.Quote(alias, "website"),
+ Contact: psql.Quote(alias, "contact"),
+ Address: psql.Quote(alias, "address"),
+ Regionid: psql.Quote(alias, "regionid"),
+ PostalCod: psql.Quote(alias, "postal_cod"),
+ Phone1: psql.Quote(alias, "phone1"),
+ Fax1: psql.Quote(alias, "fax1"),
+ Agency: psql.Quote(alias, "agency"),
+ Code1: psql.Quote(alias, "code1"),
+ City1: psql.Quote(alias, "city1"),
+ ShapeLeng: psql.Quote(alias, "shape_leng"),
+ Address2: psql.Quote(alias, "address2"),
+ GeneralMG: psql.Quote(alias, "general_mg"),
+ City2: psql.Quote(alias, "city2"),
+ PostalC1: psql.Quote(alias, "postal_c_1"),
+ Fax2: psql.Quote(alias, "fax2"),
+ Phone2: psql.Quote(alias, "phone2"),
+ ShapeLe1: psql.Quote(alias, "shape_le_1"),
+ ShapeArea: psql.Quote(alias, "shape_area"),
+ Geom: psql.Quote(alias, "geom"),
+ Geom4326: psql.Quote(alias, "geom_4326"),
+ Centroid4326: psql.Quote(alias, "centroid_4326"),
+ Extent4326: psql.Quote(alias, "extent_4326"),
}
}
type importDistrictColumns struct {
expr.ColumnsExpr
- tableAlias string
- Gid psql.Expression
- ID psql.Expression
- Website psql.Expression
- Contact psql.Expression
- Address psql.Expression
- Regionid psql.Expression
- PostalCod psql.Expression
- Phone1 psql.Expression
- Fax1 psql.Expression
- Agency psql.Expression
- Code1 psql.Expression
- City1 psql.Expression
- ShapeLeng psql.Expression
- Address2 psql.Expression
- GeneralMG psql.Expression
- City2 psql.Expression
- PostalC1 psql.Expression
- Fax2 psql.Expression
- Phone2 psql.Expression
- ShapeLe1 psql.Expression
- ShapeArea psql.Expression
- Geom psql.Expression
- Geom4326 psql.Expression
+ tableAlias string
+ Gid psql.Expression
+ ID psql.Expression
+ Website psql.Expression
+ Contact psql.Expression
+ Address psql.Expression
+ Regionid psql.Expression
+ PostalCod psql.Expression
+ Phone1 psql.Expression
+ Fax1 psql.Expression
+ Agency psql.Expression
+ Code1 psql.Expression
+ City1 psql.Expression
+ ShapeLeng psql.Expression
+ Address2 psql.Expression
+ GeneralMG psql.Expression
+ City2 psql.Expression
+ PostalC1 psql.Expression
+ Fax2 psql.Expression
+ Phone2 psql.Expression
+ ShapeLe1 psql.Expression
+ ShapeArea psql.Expression
+ Geom psql.Expression
+ Geom4326 psql.Expression
+ Centroid4326 psql.Expression
+ Extent4326 psql.Expression
}
func (c importDistrictColumns) Alias() string {
@@ -913,29 +919,31 @@ func (importDistrict0 *ImportDistrict) AttachImportDistrictGidOrganization(ctx c
}
type importDistrictWhere[Q psql.Filterable] struct {
- Gid psql.WhereMod[Q, int32]
- ID psql.WhereNullMod[Q, decimal.Decimal]
- Website psql.WhereNullMod[Q, string]
- Contact psql.WhereNullMod[Q, string]
- Address psql.WhereNullMod[Q, string]
- Regionid psql.WhereNullMod[Q, decimal.Decimal]
- PostalCod psql.WhereNullMod[Q, decimal.Decimal]
- Phone1 psql.WhereNullMod[Q, string]
- Fax1 psql.WhereNullMod[Q, string]
- Agency psql.WhereNullMod[Q, string]
- Code1 psql.WhereNullMod[Q, string]
- City1 psql.WhereNullMod[Q, string]
- ShapeLeng psql.WhereNullMod[Q, decimal.Decimal]
- Address2 psql.WhereNullMod[Q, string]
- GeneralMG psql.WhereNullMod[Q, string]
- City2 psql.WhereNullMod[Q, string]
- PostalC1 psql.WhereNullMod[Q, decimal.Decimal]
- Fax2 psql.WhereNullMod[Q, string]
- Phone2 psql.WhereNullMod[Q, string]
- ShapeLe1 psql.WhereNullMod[Q, decimal.Decimal]
- ShapeArea psql.WhereNullMod[Q, decimal.Decimal]
- Geom psql.WhereNullMod[Q, string]
- Geom4326 psql.WhereNullMod[Q, string]
+ Gid psql.WhereMod[Q, int32]
+ ID psql.WhereNullMod[Q, decimal.Decimal]
+ Website psql.WhereNullMod[Q, string]
+ Contact psql.WhereNullMod[Q, string]
+ Address psql.WhereNullMod[Q, string]
+ Regionid psql.WhereNullMod[Q, decimal.Decimal]
+ PostalCod psql.WhereNullMod[Q, decimal.Decimal]
+ Phone1 psql.WhereNullMod[Q, string]
+ Fax1 psql.WhereNullMod[Q, string]
+ Agency psql.WhereNullMod[Q, string]
+ Code1 psql.WhereNullMod[Q, string]
+ City1 psql.WhereNullMod[Q, string]
+ ShapeLeng psql.WhereNullMod[Q, decimal.Decimal]
+ Address2 psql.WhereNullMod[Q, string]
+ GeneralMG psql.WhereNullMod[Q, string]
+ City2 psql.WhereNullMod[Q, string]
+ PostalC1 psql.WhereNullMod[Q, decimal.Decimal]
+ Fax2 psql.WhereNullMod[Q, string]
+ Phone2 psql.WhereNullMod[Q, string]
+ ShapeLe1 psql.WhereNullMod[Q, decimal.Decimal]
+ ShapeArea psql.WhereNullMod[Q, decimal.Decimal]
+ Geom psql.WhereNullMod[Q, string]
+ Geom4326 psql.WhereNullMod[Q, string]
+ Centroid4326 psql.WhereNullMod[Q, string]
+ Extent4326 psql.WhereNullMod[Q, string]
}
func (importDistrictWhere[Q]) AliasedAs(alias string) importDistrictWhere[Q] {
@@ -944,29 +952,31 @@ func (importDistrictWhere[Q]) AliasedAs(alias string) importDistrictWhere[Q] {
func buildImportDistrictWhere[Q psql.Filterable](cols importDistrictColumns) importDistrictWhere[Q] {
return importDistrictWhere[Q]{
- Gid: psql.Where[Q, int32](cols.Gid),
- ID: psql.WhereNull[Q, decimal.Decimal](cols.ID),
- Website: psql.WhereNull[Q, string](cols.Website),
- Contact: psql.WhereNull[Q, string](cols.Contact),
- Address: psql.WhereNull[Q, string](cols.Address),
- Regionid: psql.WhereNull[Q, decimal.Decimal](cols.Regionid),
- PostalCod: psql.WhereNull[Q, decimal.Decimal](cols.PostalCod),
- Phone1: psql.WhereNull[Q, string](cols.Phone1),
- Fax1: psql.WhereNull[Q, string](cols.Fax1),
- Agency: psql.WhereNull[Q, string](cols.Agency),
- Code1: psql.WhereNull[Q, string](cols.Code1),
- City1: psql.WhereNull[Q, string](cols.City1),
- ShapeLeng: psql.WhereNull[Q, decimal.Decimal](cols.ShapeLeng),
- Address2: psql.WhereNull[Q, string](cols.Address2),
- GeneralMG: psql.WhereNull[Q, string](cols.GeneralMG),
- City2: psql.WhereNull[Q, string](cols.City2),
- PostalC1: psql.WhereNull[Q, decimal.Decimal](cols.PostalC1),
- Fax2: psql.WhereNull[Q, string](cols.Fax2),
- Phone2: psql.WhereNull[Q, string](cols.Phone2),
- ShapeLe1: psql.WhereNull[Q, decimal.Decimal](cols.ShapeLe1),
- ShapeArea: psql.WhereNull[Q, decimal.Decimal](cols.ShapeArea),
- Geom: psql.WhereNull[Q, string](cols.Geom),
- Geom4326: psql.WhereNull[Q, string](cols.Geom4326),
+ Gid: psql.Where[Q, int32](cols.Gid),
+ ID: psql.WhereNull[Q, decimal.Decimal](cols.ID),
+ Website: psql.WhereNull[Q, string](cols.Website),
+ Contact: psql.WhereNull[Q, string](cols.Contact),
+ Address: psql.WhereNull[Q, string](cols.Address),
+ Regionid: psql.WhereNull[Q, decimal.Decimal](cols.Regionid),
+ PostalCod: psql.WhereNull[Q, decimal.Decimal](cols.PostalCod),
+ Phone1: psql.WhereNull[Q, string](cols.Phone1),
+ Fax1: psql.WhereNull[Q, string](cols.Fax1),
+ Agency: psql.WhereNull[Q, string](cols.Agency),
+ Code1: psql.WhereNull[Q, string](cols.Code1),
+ City1: psql.WhereNull[Q, string](cols.City1),
+ ShapeLeng: psql.WhereNull[Q, decimal.Decimal](cols.ShapeLeng),
+ Address2: psql.WhereNull[Q, string](cols.Address2),
+ GeneralMG: psql.WhereNull[Q, string](cols.GeneralMG),
+ City2: psql.WhereNull[Q, string](cols.City2),
+ PostalC1: psql.WhereNull[Q, decimal.Decimal](cols.PostalC1),
+ Fax2: psql.WhereNull[Q, string](cols.Fax2),
+ Phone2: psql.WhereNull[Q, string](cols.Phone2),
+ ShapeLe1: psql.WhereNull[Q, decimal.Decimal](cols.ShapeLe1),
+ ShapeArea: psql.WhereNull[Q, decimal.Decimal](cols.ShapeArea),
+ Geom: psql.WhereNull[Q, string](cols.Geom),
+ Geom4326: psql.WhereNull[Q, string](cols.Geom4326),
+ Centroid4326: psql.WhereNull[Q, string](cols.Centroid4326),
+ Extent4326: psql.WhereNull[Q, string](cols.Extent4326),
}
}
diff --git a/html/static/js/map-district.js b/html/static/js/map-district.js
new file mode 100644
index 00000000..9985fab8
--- /dev/null
+++ b/html/static/js/map-district.js
@@ -0,0 +1,135 @@
+// A test of maplibre-gl in a custom element
+class MapDistrict extends HTMLElement {
+ constructor() {
+ super();
+
+ // Create a shadow DOM
+ this.attachShadow({ mode: "open" });
+
+ // Initial render
+ this.render();
+
+ this._map = null;
+
+ // markers shown on the map
+ this._markers = [];
+ }
+
+ // Lifecycle: when element is added to the DOM
+ connectedCallback() {
+ // Initialize the map when the element is added to the DOM
+ setTimeout(() => this._initializeMap(), 0);
+ }
+
+ disconnectedCallback() {
+ if (this._map) {
+ this._map.remove();
+ }
+ }
+
+ _initializeMap() {
+ const apiKey = this.getAttribute("api-key");
+ const centroid = JSON.parse(this.getAttribute("centroid"));
+ const csv_file = this.getAttribute("csv-file");
+ const district_id = this.getAttribute("district-id");
+ const lat = Number(this.getAttribute("latitude") || 36.2);
+ const lng = Number(this.getAttribute("longitude") || -119.2);
+ const mapElement = this.shadowRoot.querySelector("#map");
+ const tegola = this.getAttribute("tegola");
+ const xmin = parseFloat(this.getAttribute("xmin"));
+ const ymin = parseFloat(this.getAttribute("ymin"));
+ const xmax = parseFloat(this.getAttribute("xmax"));
+ const ymax = parseFloat(this.getAttribute("ymax"));
+ const bounds = [
+ [xmin, ymin],
+ [xmax, ymax],
+ ];
+ console.log("fitting", bounds);
+ this._map = new maplibregl.Map({
+ container: mapElement,
+ center: centroid.coordinates,
+ style: "https://tiles.stadiamaps.com/styles/alidade_smooth.json", // Style URL; see our documentation for more options
+ }).fitBounds(bounds, {
+ padding: { top: 10, bottom: 10, left: 10, right: 10 },
+ });
+ this._map.on("load", () => {
+ this._map.addSource("tegola-nidus", {
+ type: "vector",
+ tiles: [
+ `${tegola}maps/district/{z}/{x}/{y}?district_id=${district_id}`,
+ ],
+ });
+ this._map.addLayer({
+ id: "bounds",
+ source: "tegola-nidus",
+ "source-layer": "bounds",
+ type: "fill",
+ paint: {
+ "fill-opacity": 0.4,
+ "fill-color": "#dc3545",
+ },
+ });
+ });
+ }
+
+ // Initial render of component
+ render() {
+ this.shadowRoot.innerHTML = `
+
+
+
+ `;
+ }
+
+ addLayer(a) {
+ return this._map.addLayer(a);
+ }
+ addSource(a, b) {
+ return this._map.addSource(a, b);
+ }
+ jumpTo(args) {
+ return this._map.jumpTo(args);
+ }
+ on(a, b) {
+ return this._map.on(a, b);
+ }
+ once(a, b) {
+ return this._map.once(a, b);
+ }
+ queryRenderedFeatures(a) {
+ return this._map.queryRenderedFeatures(a);
+ }
+
+ SetLayoutProperty(layout, property, value) {
+ return this._map.setLayoutProperty(layout, property, value);
+ }
+}
+
+customElements.define("map-district", MapDistrict);
diff --git a/html/template/sync/setting-district.html b/html/template/sync/setting-district.html
new file mode 100644
index 00000000..97632f31
--- /dev/null
+++ b/html/template/sync/setting-district.html
@@ -0,0 +1,136 @@
+{{ template "sync/layout/authenticated.html" . }}
+
+{{ define "title" }}Settings - Integrations{{ end }}
+{{ define "extraheader" }}
+
+
+
+{{ end }}
+{{ define "content" }}
+
+
+
+
+
District Settings
+ Save Changes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Total Area (square kilometers)
+
+
+
+ Counties Served
+
+ Franklin County
+ Jefferson County
+ Washington County
+ Lincoln County
+ Adams County
+
+ Hold Ctrl (or Cmd) to select multiple counties
+
+
+
+
+
+
+
+
+ Cancel
+ Save Changes
+
+
+
+
+{{ end }}
diff --git a/html/template/sync/settings.html b/html/template/sync/settings.html
index 4f9a1713..764fc83f 100644
--- a/html/template/sync/settings.html
+++ b/html/template/sync/settings.html
@@ -101,7 +101,7 @@
Manage your district location and information.
-
+
Manage District
diff --git a/sync/routes.go b/sync/routes.go
index 7c9bbcd3..22b8fedb 100644
--- a/sync/routes.go
+++ b/sync/routes.go
@@ -67,6 +67,7 @@ func Router() chi.Router {
r.Method("GET", "/pool/upload/{id}", auth.NewEnsureAuth(getPoolUploadByID))
r.Method("POST", "/pool/upload", auth.NewEnsureAuth(postPoolUpload))
r.Method("GET", "/setting", auth.NewEnsureAuth(getSetting))
+ r.Method("GET", "/setting/district", auth.NewEnsureAuth(getSettingDistrict))
r.Method("GET", "/setting/integration", auth.NewEnsureAuth(getSettingIntegration))
r.Method("GET", "/signout", auth.NewEnsureAuth(getSignout))
r.Method("GET", "/source/{globalid}", auth.NewEnsureAuth(getSource))
diff --git a/sync/setting.go b/sync/setting.go
index d972f9c7..ba796e5c 100644
--- a/sync/setting.go
+++ b/sync/setting.go
@@ -3,12 +3,32 @@ package sync
import (
"net/http"
+ "github.com/Gleipnir-Technology/bob"
+ "github.com/Gleipnir-Technology/bob/dialect/psql"
+ "github.com/Gleipnir-Technology/bob/dialect/psql/sm"
"github.com/Gleipnir-Technology/nidus-sync/arcgis"
+ "github.com/Gleipnir-Technology/nidus-sync/db"
"github.com/Gleipnir-Technology/nidus-sync/db/models"
"github.com/Gleipnir-Technology/nidus-sync/html"
+ //"github.com/rs/zerolog/log"
+ "github.com/stephenafamo/scan"
)
-type ContentSettingIntegration struct {
+type contentDistrict struct {
+ Centroid string `db:"st_asgeojson"`
+ GID int32 `db:"gid"`
+ XMin float32 `db:"st_xmin"`
+ YMin float32 `db:"st_ymin"`
+ XMax float32 `db:"st_xmax"`
+ YMax float32 `db:"st_ymax"`
+}
+type contentSettingDistrict struct {
+ District contentDistrict
+ URL ContentURL
+ User User
+}
+
+type contentSettingIntegration struct {
ArcGISOAuth *models.OauthToken
URL ContentURL
User User
@@ -26,6 +46,42 @@ func getSetting(w http.ResponseWriter, r *http.Request, u *models.User) {
}
html.RenderOrError(w, "sync/settings.html", data)
}
+func getSettingDistrict(w http.ResponseWriter, r *http.Request, u *models.User) {
+ ctx := r.Context()
+ userContent, err := contentForUser(ctx, u)
+ if err != nil {
+ respondError(w, "Failed to get user content", err, http.StatusInternalServerError)
+ return
+ }
+ org, err := u.Organization().One(ctx, db.PGInstance.BobDB)
+ var district contentDistrict
+ gid := int32(0)
+ if org.ImportDistrictGid.IsValue() {
+ gid = org.ImportDistrictGid.MustGet()
+ district, err = bob.One[contentDistrict](ctx, db.PGInstance.BobDB, psql.Select(
+ sm.From("import.district"),
+ sm.Columns(
+ "gid",
+ psql.F("ST_AsGeoJSON", "centroid_4326"),
+ psql.F("ST_XMin", "extent_4326"),
+ psql.F("ST_YMin", "extent_4326"),
+ psql.F("ST_XMax", "extent_4326"),
+ psql.F("ST_YMax", "extent_4326"),
+ ),
+ sm.Where(psql.Quote("gid").EQ(psql.Arg(gid))),
+ ), scan.StructMapper[contentDistrict]())
+ if err != nil {
+ respondError(w, "Failed to get extents", err, http.StatusInternalServerError)
+ return
+ }
+ }
+ data := contentSettingDistrict{
+ District: district,
+ URL: newContentURL(),
+ User: userContent,
+ }
+ html.RenderOrError(w, "sync/setting-district.html", data)
+}
func getSettingIntegration(w http.ResponseWriter, r *http.Request, u *models.User) {
ctx := r.Context()
userContent, err := contentForUser(ctx, u)
@@ -38,7 +94,7 @@ func getSettingIntegration(w http.ResponseWriter, r *http.Request, u *models.Use
respondError(w, "Failed to get oauth", err, http.StatusInternalServerError)
return
}
- data := ContentSettingIntegration{
+ data := contentSettingIntegration{
ArcGISOAuth: oauth,
URL: newContentURL(),
User: userContent,
diff --git a/sync/url.go b/sync/url.go
index 996a1d37..b8ebbb09 100644
--- a/sync/url.go
+++ b/sync/url.go
@@ -9,6 +9,7 @@ type ContentURL struct {
PoolCSVUpload string
SamplePoolCSV string
Setting string
+ SettingDistrict string
SettingIntegration string
SettingPesticide string
SettingPesticideAdd string
@@ -23,6 +24,7 @@ func newContentURL() ContentURL {
PoolCSVUpload: config.MakeURLNidus("/pool/upload"),
SamplePoolCSV: config.MakeURLNidus("/static/file/sample-pool.csv"),
Setting: config.MakeURLNidus("/setting"),
+ SettingDistrict: config.MakeURLNidus("/setting/district"),
SettingIntegration: config.MakeURLNidus("/setting/integration"),
SettingPesticide: config.MakeURLNidus("/setting/pesticide"),
SettingPesticideAdd: config.MakeURLNidus("/setting/pesticide/add"),
diff --git a/tools/drop-and-recreate.sql b/tools/drop-and-recreate.sql
index ebe1470f..d2c37fce 100644
--- a/tools/drop-and-recreate.sql
+++ b/tools/drop-and-recreate.sql
@@ -18,3 +18,5 @@ GRANT SELECT ON publicreport.report_location TO "tegola";
GRANT ALL PRIVILEGES ON SCHEMA public TO $1;
-- do import of district data
ALTER TABLE import.district ADD COLUMN geom_4326 geometry(MultiPolygon,4326) GENERATED ALWAYS AS (ST_Transform(geom, 4326)) STORED;
+ALTER TABLE import.district ADD COLUMN centroid_4326 geometry(Point,4326) GENERATED ALWAYS AS (ST_Transform(ST_Centroid(geom), 4326)) STORED;
+ALTER TABLE import.district ADD COLUMN extent_4326 geometry(Polygon,4326) GENERATED ALWAYS AS (ST_Transform(ST_Envelope(geom), 4326)) STORED;