Add district setting page and display of district boundary

This commit is contained in:
Eli Ribble 2026-02-16 15:03:26 +00:00
parent 4c8da3b96a
commit a1cc2dbaff
No known key found for this signature in database
11 changed files with 648 additions and 168 deletions

View file

@ -222,6 +222,24 @@ var ImportDistricts = Table[
Generated: true, Generated: true,
AutoIncr: false, 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{ Indexes: importDistrictIndexes{
DistrictPkey: index{ DistrictPkey: index{
@ -269,34 +287,36 @@ var ImportDistricts = Table[
} }
type importDistrictColumns struct { type importDistrictColumns struct {
Gid column Gid column
ID column ID column
Website column Website column
Contact column Contact column
Address column Address column
Regionid column Regionid column
PostalCod column PostalCod column
Phone1 column Phone1 column
Fax1 column Fax1 column
Agency column Agency column
Code1 column Code1 column
City1 column City1 column
ShapeLeng column ShapeLeng column
Address2 column Address2 column
GeneralMG column GeneralMG column
City2 column City2 column
PostalC1 column PostalC1 column
Fax2 column Fax2 column
Phone2 column Phone2 column
ShapeLe1 column ShapeLe1 column
ShapeArea column ShapeArea column
Geom column Geom column
Geom4326 column Geom4326 column
Centroid4326 column
Extent4326 column
} }
func (c importDistrictColumns) AsSlice() []column { func (c importDistrictColumns) AsSlice() []column {
return []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,
} }
} }

View file

@ -2622,6 +2622,8 @@ func (f *Factory) FromExistingImportDistrict(m *models.ImportDistrict) *ImportDi
o.ShapeArea = func() null.Val[decimal.Decimal] { return m.ShapeArea } o.ShapeArea = func() null.Val[decimal.Decimal] { return m.ShapeArea }
o.Geom = func() null.Val[string] { return m.Geom } o.Geom = func() null.Val[string] { return m.Geom }
o.Geom4326 = func() null.Val[string] { return m.Geom4326 } 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() ctx := context.Background()
if m.R.ImportDistrictGidOrganization != nil { if m.R.ImportDistrictGidOrganization != nil {

View file

@ -37,29 +37,31 @@ func (mods ImportDistrictModSlice) Apply(ctx context.Context, n *ImportDistrictT
// ImportDistrictTemplate is an object representing the database table. // ImportDistrictTemplate is an object representing the database table.
// all columns are optional and should be set by mods // all columns are optional and should be set by mods
type ImportDistrictTemplate struct { type ImportDistrictTemplate struct {
Gid func() int32 Gid func() int32
ID func() null.Val[decimal.Decimal] ID func() null.Val[decimal.Decimal]
Website func() null.Val[string] Website func() null.Val[string]
Contact func() null.Val[string] Contact func() null.Val[string]
Address func() null.Val[string] Address func() null.Val[string]
Regionid func() null.Val[decimal.Decimal] Regionid func() null.Val[decimal.Decimal]
PostalCod func() null.Val[decimal.Decimal] PostalCod func() null.Val[decimal.Decimal]
Phone1 func() null.Val[string] Phone1 func() null.Val[string]
Fax1 func() null.Val[string] Fax1 func() null.Val[string]
Agency func() null.Val[string] Agency func() null.Val[string]
Code1 func() null.Val[string] Code1 func() null.Val[string]
City1 func() null.Val[string] City1 func() null.Val[string]
ShapeLeng func() null.Val[decimal.Decimal] ShapeLeng func() null.Val[decimal.Decimal]
Address2 func() null.Val[string] Address2 func() null.Val[string]
GeneralMG func() null.Val[string] GeneralMG func() null.Val[string]
City2 func() null.Val[string] City2 func() null.Val[string]
PostalC1 func() null.Val[decimal.Decimal] PostalC1 func() null.Val[decimal.Decimal]
Fax2 func() null.Val[string] Fax2 func() null.Val[string]
Phone2 func() null.Val[string] Phone2 func() null.Val[string]
ShapeLe1 func() null.Val[decimal.Decimal] ShapeLe1 func() null.Val[decimal.Decimal]
ShapeArea func() null.Val[decimal.Decimal] ShapeArea func() null.Val[decimal.Decimal]
Geom func() null.Val[string] Geom func() null.Val[string]
Geom4326 func() null.Val[string] Geom4326 func() null.Val[string]
Centroid4326 func() null.Val[string]
Extent4326 func() null.Val[string]
r importDistrictR r importDistrictR
f *Factory f *Factory
@ -277,6 +279,12 @@ func (o ImportDistrictTemplate) Build() *models.ImportDistrict {
if o.Geom4326 != nil { if o.Geom4326 != nil {
m.Geom4326 = o.Geom4326() m.Geom4326 = o.Geom4326()
} }
if o.Centroid4326 != nil {
m.Centroid4326 = o.Centroid4326()
}
if o.Extent4326 != nil {
m.Extent4326 = o.Extent4326()
}
o.setModelRels(m) o.setModelRels(m)
@ -439,6 +447,8 @@ func (m importDistrictMods) RandomizeAllColumns(f *faker.Faker) ImportDistrictMo
ImportDistrictMods.RandomShapeArea(f), ImportDistrictMods.RandomShapeArea(f),
ImportDistrictMods.RandomGeom(f), ImportDistrictMods.RandomGeom(f),
ImportDistrictMods.RandomGeom4326(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 { func (m importDistrictMods) WithParentsCascading() ImportDistrictMod {
return ImportDistrictModFunc(func(ctx context.Context, o *ImportDistrictTemplate) { return ImportDistrictModFunc(func(ctx context.Context, o *ImportDistrictTemplate) {
if isDone, _ := importDistrictWithParentsCascadingCtx.Value(ctx); isDone { if isDone, _ := importDistrictWithParentsCascadingCtx.Value(ctx); isDone {

View file

@ -26,29 +26,31 @@ import (
// ImportDistrict is an object representing the database table. // ImportDistrict is an object representing the database table.
type ImportDistrict struct { type ImportDistrict struct {
Gid int32 `db:"gid,pk" ` Gid int32 `db:"gid,pk" `
ID null.Val[decimal.Decimal] `db:"id" ` ID null.Val[decimal.Decimal] `db:"id" `
Website null.Val[string] `db:"website" ` Website null.Val[string] `db:"website" `
Contact null.Val[string] `db:"contact" ` Contact null.Val[string] `db:"contact" `
Address null.Val[string] `db:"address" ` Address null.Val[string] `db:"address" `
Regionid null.Val[decimal.Decimal] `db:"regionid" ` Regionid null.Val[decimal.Decimal] `db:"regionid" `
PostalCod null.Val[decimal.Decimal] `db:"postal_cod" ` PostalCod null.Val[decimal.Decimal] `db:"postal_cod" `
Phone1 null.Val[string] `db:"phone1" ` Phone1 null.Val[string] `db:"phone1" `
Fax1 null.Val[string] `db:"fax1" ` Fax1 null.Val[string] `db:"fax1" `
Agency null.Val[string] `db:"agency" ` Agency null.Val[string] `db:"agency" `
Code1 null.Val[string] `db:"code1" ` Code1 null.Val[string] `db:"code1" `
City1 null.Val[string] `db:"city1" ` City1 null.Val[string] `db:"city1" `
ShapeLeng null.Val[decimal.Decimal] `db:"shape_leng" ` ShapeLeng null.Val[decimal.Decimal] `db:"shape_leng" `
Address2 null.Val[string] `db:"address2" ` Address2 null.Val[string] `db:"address2" `
GeneralMG null.Val[string] `db:"general_mg" ` GeneralMG null.Val[string] `db:"general_mg" `
City2 null.Val[string] `db:"city2" ` City2 null.Val[string] `db:"city2" `
PostalC1 null.Val[decimal.Decimal] `db:"postal_c_1" ` PostalC1 null.Val[decimal.Decimal] `db:"postal_c_1" `
Fax2 null.Val[string] `db:"fax2" ` Fax2 null.Val[string] `db:"fax2" `
Phone2 null.Val[string] `db:"phone2" ` Phone2 null.Val[string] `db:"phone2" `
ShapeLe1 null.Val[decimal.Decimal] `db:"shape_le_1" ` ShapeLe1 null.Val[decimal.Decimal] `db:"shape_le_1" `
ShapeArea null.Val[decimal.Decimal] `db:"shape_area" ` ShapeArea null.Val[decimal.Decimal] `db:"shape_area" `
Geom null.Val[string] `db:"geom" ` Geom null.Val[string] `db:"geom" `
Geom4326 null.Val[string] `db:"geom_4326,generated" ` 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:"-" ` R importDistrictR `db:"-" `
} }
@ -71,61 +73,65 @@ type importDistrictR struct {
func buildImportDistrictColumns(alias string) importDistrictColumns { func buildImportDistrictColumns(alias string) importDistrictColumns {
return importDistrictColumns{ return importDistrictColumns{
ColumnsExpr: expr.NewColumnsExpr( 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"), ).WithParent("import.district"),
tableAlias: alias, tableAlias: alias,
Gid: psql.Quote(alias, "gid"), Gid: psql.Quote(alias, "gid"),
ID: psql.Quote(alias, "id"), ID: psql.Quote(alias, "id"),
Website: psql.Quote(alias, "website"), Website: psql.Quote(alias, "website"),
Contact: psql.Quote(alias, "contact"), Contact: psql.Quote(alias, "contact"),
Address: psql.Quote(alias, "address"), Address: psql.Quote(alias, "address"),
Regionid: psql.Quote(alias, "regionid"), Regionid: psql.Quote(alias, "regionid"),
PostalCod: psql.Quote(alias, "postal_cod"), PostalCod: psql.Quote(alias, "postal_cod"),
Phone1: psql.Quote(alias, "phone1"), Phone1: psql.Quote(alias, "phone1"),
Fax1: psql.Quote(alias, "fax1"), Fax1: psql.Quote(alias, "fax1"),
Agency: psql.Quote(alias, "agency"), Agency: psql.Quote(alias, "agency"),
Code1: psql.Quote(alias, "code1"), Code1: psql.Quote(alias, "code1"),
City1: psql.Quote(alias, "city1"), City1: psql.Quote(alias, "city1"),
ShapeLeng: psql.Quote(alias, "shape_leng"), ShapeLeng: psql.Quote(alias, "shape_leng"),
Address2: psql.Quote(alias, "address2"), Address2: psql.Quote(alias, "address2"),
GeneralMG: psql.Quote(alias, "general_mg"), GeneralMG: psql.Quote(alias, "general_mg"),
City2: psql.Quote(alias, "city2"), City2: psql.Quote(alias, "city2"),
PostalC1: psql.Quote(alias, "postal_c_1"), PostalC1: psql.Quote(alias, "postal_c_1"),
Fax2: psql.Quote(alias, "fax2"), Fax2: psql.Quote(alias, "fax2"),
Phone2: psql.Quote(alias, "phone2"), Phone2: psql.Quote(alias, "phone2"),
ShapeLe1: psql.Quote(alias, "shape_le_1"), ShapeLe1: psql.Quote(alias, "shape_le_1"),
ShapeArea: psql.Quote(alias, "shape_area"), ShapeArea: psql.Quote(alias, "shape_area"),
Geom: psql.Quote(alias, "geom"), Geom: psql.Quote(alias, "geom"),
Geom4326: psql.Quote(alias, "geom_4326"), Geom4326: psql.Quote(alias, "geom_4326"),
Centroid4326: psql.Quote(alias, "centroid_4326"),
Extent4326: psql.Quote(alias, "extent_4326"),
} }
} }
type importDistrictColumns struct { type importDistrictColumns struct {
expr.ColumnsExpr expr.ColumnsExpr
tableAlias string tableAlias string
Gid psql.Expression Gid psql.Expression
ID psql.Expression ID psql.Expression
Website psql.Expression Website psql.Expression
Contact psql.Expression Contact psql.Expression
Address psql.Expression Address psql.Expression
Regionid psql.Expression Regionid psql.Expression
PostalCod psql.Expression PostalCod psql.Expression
Phone1 psql.Expression Phone1 psql.Expression
Fax1 psql.Expression Fax1 psql.Expression
Agency psql.Expression Agency psql.Expression
Code1 psql.Expression Code1 psql.Expression
City1 psql.Expression City1 psql.Expression
ShapeLeng psql.Expression ShapeLeng psql.Expression
Address2 psql.Expression Address2 psql.Expression
GeneralMG psql.Expression GeneralMG psql.Expression
City2 psql.Expression City2 psql.Expression
PostalC1 psql.Expression PostalC1 psql.Expression
Fax2 psql.Expression Fax2 psql.Expression
Phone2 psql.Expression Phone2 psql.Expression
ShapeLe1 psql.Expression ShapeLe1 psql.Expression
ShapeArea psql.Expression ShapeArea psql.Expression
Geom psql.Expression Geom psql.Expression
Geom4326 psql.Expression Geom4326 psql.Expression
Centroid4326 psql.Expression
Extent4326 psql.Expression
} }
func (c importDistrictColumns) Alias() string { func (c importDistrictColumns) Alias() string {
@ -913,29 +919,31 @@ func (importDistrict0 *ImportDistrict) AttachImportDistrictGidOrganization(ctx c
} }
type importDistrictWhere[Q psql.Filterable] struct { type importDistrictWhere[Q psql.Filterable] struct {
Gid psql.WhereMod[Q, int32] Gid psql.WhereMod[Q, int32]
ID psql.WhereNullMod[Q, decimal.Decimal] ID psql.WhereNullMod[Q, decimal.Decimal]
Website psql.WhereNullMod[Q, string] Website psql.WhereNullMod[Q, string]
Contact psql.WhereNullMod[Q, string] Contact psql.WhereNullMod[Q, string]
Address psql.WhereNullMod[Q, string] Address psql.WhereNullMod[Q, string]
Regionid psql.WhereNullMod[Q, decimal.Decimal] Regionid psql.WhereNullMod[Q, decimal.Decimal]
PostalCod psql.WhereNullMod[Q, decimal.Decimal] PostalCod psql.WhereNullMod[Q, decimal.Decimal]
Phone1 psql.WhereNullMod[Q, string] Phone1 psql.WhereNullMod[Q, string]
Fax1 psql.WhereNullMod[Q, string] Fax1 psql.WhereNullMod[Q, string]
Agency psql.WhereNullMod[Q, string] Agency psql.WhereNullMod[Q, string]
Code1 psql.WhereNullMod[Q, string] Code1 psql.WhereNullMod[Q, string]
City1 psql.WhereNullMod[Q, string] City1 psql.WhereNullMod[Q, string]
ShapeLeng psql.WhereNullMod[Q, decimal.Decimal] ShapeLeng psql.WhereNullMod[Q, decimal.Decimal]
Address2 psql.WhereNullMod[Q, string] Address2 psql.WhereNullMod[Q, string]
GeneralMG psql.WhereNullMod[Q, string] GeneralMG psql.WhereNullMod[Q, string]
City2 psql.WhereNullMod[Q, string] City2 psql.WhereNullMod[Q, string]
PostalC1 psql.WhereNullMod[Q, decimal.Decimal] PostalC1 psql.WhereNullMod[Q, decimal.Decimal]
Fax2 psql.WhereNullMod[Q, string] Fax2 psql.WhereNullMod[Q, string]
Phone2 psql.WhereNullMod[Q, string] Phone2 psql.WhereNullMod[Q, string]
ShapeLe1 psql.WhereNullMod[Q, decimal.Decimal] ShapeLe1 psql.WhereNullMod[Q, decimal.Decimal]
ShapeArea psql.WhereNullMod[Q, decimal.Decimal] ShapeArea psql.WhereNullMod[Q, decimal.Decimal]
Geom psql.WhereNullMod[Q, string] Geom psql.WhereNullMod[Q, string]
Geom4326 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] { 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] { func buildImportDistrictWhere[Q psql.Filterable](cols importDistrictColumns) importDistrictWhere[Q] {
return importDistrictWhere[Q]{ return importDistrictWhere[Q]{
Gid: psql.Where[Q, int32](cols.Gid), Gid: psql.Where[Q, int32](cols.Gid),
ID: psql.WhereNull[Q, decimal.Decimal](cols.ID), ID: psql.WhereNull[Q, decimal.Decimal](cols.ID),
Website: psql.WhereNull[Q, string](cols.Website), Website: psql.WhereNull[Q, string](cols.Website),
Contact: psql.WhereNull[Q, string](cols.Contact), Contact: psql.WhereNull[Q, string](cols.Contact),
Address: psql.WhereNull[Q, string](cols.Address), Address: psql.WhereNull[Q, string](cols.Address),
Regionid: psql.WhereNull[Q, decimal.Decimal](cols.Regionid), Regionid: psql.WhereNull[Q, decimal.Decimal](cols.Regionid),
PostalCod: psql.WhereNull[Q, decimal.Decimal](cols.PostalCod), PostalCod: psql.WhereNull[Q, decimal.Decimal](cols.PostalCod),
Phone1: psql.WhereNull[Q, string](cols.Phone1), Phone1: psql.WhereNull[Q, string](cols.Phone1),
Fax1: psql.WhereNull[Q, string](cols.Fax1), Fax1: psql.WhereNull[Q, string](cols.Fax1),
Agency: psql.WhereNull[Q, string](cols.Agency), Agency: psql.WhereNull[Q, string](cols.Agency),
Code1: psql.WhereNull[Q, string](cols.Code1), Code1: psql.WhereNull[Q, string](cols.Code1),
City1: psql.WhereNull[Q, string](cols.City1), City1: psql.WhereNull[Q, string](cols.City1),
ShapeLeng: psql.WhereNull[Q, decimal.Decimal](cols.ShapeLeng), ShapeLeng: psql.WhereNull[Q, decimal.Decimal](cols.ShapeLeng),
Address2: psql.WhereNull[Q, string](cols.Address2), Address2: psql.WhereNull[Q, string](cols.Address2),
GeneralMG: psql.WhereNull[Q, string](cols.GeneralMG), GeneralMG: psql.WhereNull[Q, string](cols.GeneralMG),
City2: psql.WhereNull[Q, string](cols.City2), City2: psql.WhereNull[Q, string](cols.City2),
PostalC1: psql.WhereNull[Q, decimal.Decimal](cols.PostalC1), PostalC1: psql.WhereNull[Q, decimal.Decimal](cols.PostalC1),
Fax2: psql.WhereNull[Q, string](cols.Fax2), Fax2: psql.WhereNull[Q, string](cols.Fax2),
Phone2: psql.WhereNull[Q, string](cols.Phone2), Phone2: psql.WhereNull[Q, string](cols.Phone2),
ShapeLe1: psql.WhereNull[Q, decimal.Decimal](cols.ShapeLe1), ShapeLe1: psql.WhereNull[Q, decimal.Decimal](cols.ShapeLe1),
ShapeArea: psql.WhereNull[Q, decimal.Decimal](cols.ShapeArea), ShapeArea: psql.WhereNull[Q, decimal.Decimal](cols.ShapeArea),
Geom: psql.WhereNull[Q, string](cols.Geom), Geom: psql.WhereNull[Q, string](cols.Geom),
Geom4326: psql.WhereNull[Q, string](cols.Geom4326), Geom4326: psql.WhereNull[Q, string](cols.Geom4326),
Centroid4326: psql.WhereNull[Q, string](cols.Centroid4326),
Extent4326: psql.WhereNull[Q, string](cols.Extent4326),
} }
} }

View file

@ -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 = `
<style>
@import url('//unpkg.com/maplibre-gl@5.0.1/dist/maplibre-gl.css');
.mapboxgl-ctrl-bottom-right {
display: none;
}
.map-container {
background-color: #e9ecef;
border-radius: 10px;
box-shadow: 0 4px 6px rgba(0,0,0,0.05);
height: 500px;
display: flex;
align-items: center;
justify-content: center;
margin-top: 20px;
}
#map {
height: 500px;
width:100%;
margin-bottom: 10px;
}
#map img {
max-width: none;
min-width: 0px;
height: auto;
}
</style>
<div id="map-container" class="map-container">
<div id="map"></div>
</div>
`;
}
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);

View file

@ -0,0 +1,136 @@
{{ template "sync/layout/authenticated.html" . }}
{{ define "title" }}Settings - Integrations{{ end }}
{{ define "extraheader" }}
<script
type="text/javascript"
src="//unpkg.com/maplibre-gl@5.0.1/dist/maplibre-gl.js"
></script>
<script src="/static/js/map-district.js"></script>
<style>
.settings-card {
box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);
margin-bottom: 30px;
}
</style>
{{ end }}
{{ define "content" }}
<div class="container py-4">
<div class="row">
<div class="col-12">
<div class="d-flex justify-content-between align-items-center mb-4">
<h1><i class="bi bi-geo-alt-fill text-primary me-2"></i> District Settings</h1>
<button class="btn btn-primary"><i class="bi bi-save me-2"></i>Save Changes</button>
</div>
<map-district
district-id="{{.District.GID}}"
centroid="{{.District.Centroid|json}}"
xmin="{{.District.XMin}}"
ymin="{{.District.YMin}}"
xmax="{{.District.XMax}}"
ymax="{{.District.YMax}}"
tegola="{{.URL.Tegola}}"></map-district>
<div class="row">
<!-- Basic Information -->
<div class="col-md-6">
<div class="card settings-card">
<div class="card-header bg-light">
<h5><i class="bi bi-building me-2"></i> Organization Information</h5>
</div>
<div class="card-body">
<div class="mb-3">
<label for="agencyName" class="form-label"><i class="bi bi-briefcase me-1"></i> Agency Name</label>
<input type="text" class="form-control" id="agencyName" value="Central District Water Authority">
</div>
<div class="mb-3">
<label for="website" class="form-label"><i class="bi bi-globe me-1"></i> Website</label>
<input type="url" class="form-control" id="website" value="https://cdwa.example.org">
</div>
<div class="mb-3">
<label for="generalManager" class="form-label"><i class="bi bi-person-badge me-1"></i> General Manager Name</label>
<input type="text" class="form-control" id="generalManager" value="Jane Smithson">
</div>
<div class="mb-3">
<label for="contactPerson" class="form-label"><i class="bi bi-person me-1"></i> Contact Person</label>
<input type="text" class="form-control" id="contactPerson" value="John Doe">
</div>
</div>
</div>
</div>
<!-- Contact Information -->
<div class="col-md-6">
<div class="card settings-card">
<div class="card-header bg-light">
<h5><i class="bi bi-telephone me-2"></i> Contact Information</h5>
</div>
<div class="card-body">
<div class="mb-3">
<label for="address" class="form-label"><i class="bi bi-geo me-1"></i> Address</label>
<input type="text" class="form-control" id="address" value="123 Main Street, Suite 400">
</div>
<div class="row">
<div class="col-md-6 mb-3">
<label for="city" class="form-label"><i class="bi bi-building me-1"></i> City</label>
<input type="text" class="form-control" id="city" value="Centerville">
</div>
<div class="col-md-6 mb-3">
<label for="postalCode" class="form-label"><i class="bi bi-mailbox me-1"></i> Postal Code</label>
<input type="text" class="form-control" id="postalCode" value="12345">
</div>
</div>
<div class="mb-3">
<label for="phoneNumber" class="form-label"><i class="bi bi-telephone me-1"></i> Phone Number (Primary)</label>
<input type="tel" class="form-control" id="phoneNumber" value="(555) 123-4567">
</div>
<div class="mb-3">
<label for="secondaryPhone" class="form-label"><i class="bi bi-telephone-plus me-1"></i> Phone Number (Secondary)</label>
<input type="tel" class="form-control" id="secondaryPhone" value="(555) 987-6543">
</div>
<div class="mb-3">
<label for="faxNumber" class="form-label"><i class="bi bi-printer me-1"></i> Fax Number</label>
<input type="tel" class="form-control" id="faxNumber" value="(555) 765-4321">
</div>
</div>
</div>
</div>
<!-- District Coverage Information -->
<div class="col-12">
<div class="card settings-card">
<div class="card-header bg-light">
<h5><i class="bi bi-map me-2"></i> District Coverage</h5>
</div>
<div class="card-body">
<div class="row">
<div class="col-md-6 mb-3">
<label for="totalArea" class="form-label"><i class="bi bi-rulers me-1"></i> Total Area (square kilometers)</label>
<input type="number" class="form-control" id="totalArea" value="1250">
</div>
<div class="col-md-6 mb-3">
<label for="countiesServed" class="form-label"><i class="bi bi-pin-map me-1"></i> Counties Served</label>
<select class="form-select" id="countiesServed" multiple size="3">
<option selected>Franklin County</option>
<option selected>Jefferson County</option>
<option selected>Washington County</option>
<option>Lincoln County</option>
<option>Adams County</option>
</select>
<small class="form-text text-muted">Hold Ctrl (or Cmd) to select multiple counties</small>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="d-flex justify-content-end mt-3">
<button class="btn btn-secondary me-2"><i class="bi bi-x-circle me-1"></i> Cancel</button>
<button class="btn btn-primary"><i class="bi bi-save me-1"></i> Save Changes</button>
</div>
</div>
</div>
</div>
{{ end }}

View file

@ -101,7 +101,7 @@
Manage your district location and information. Manage your district location and information.
</p> </p>
<div class="d-flex justify-content-between align-items-center"> <div class="d-flex justify-content-between align-items-center">
<a href="equipment.html" class="btn btn-outline-danger"> <a href="{{ .URL.SettingDistrict }}" class="btn btn-outline-danger">
Manage District Manage District
<i class="bi bi-arrow-right ms-1"></i> <i class="bi bi-arrow-right ms-1"></i>
</a> </a>

View file

@ -67,6 +67,7 @@ func Router() chi.Router {
r.Method("GET", "/pool/upload/{id}", auth.NewEnsureAuth(getPoolUploadByID)) r.Method("GET", "/pool/upload/{id}", auth.NewEnsureAuth(getPoolUploadByID))
r.Method("POST", "/pool/upload", auth.NewEnsureAuth(postPoolUpload)) r.Method("POST", "/pool/upload", auth.NewEnsureAuth(postPoolUpload))
r.Method("GET", "/setting", auth.NewEnsureAuth(getSetting)) 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", "/setting/integration", auth.NewEnsureAuth(getSettingIntegration))
r.Method("GET", "/signout", auth.NewEnsureAuth(getSignout)) r.Method("GET", "/signout", auth.NewEnsureAuth(getSignout))
r.Method("GET", "/source/{globalid}", auth.NewEnsureAuth(getSource)) r.Method("GET", "/source/{globalid}", auth.NewEnsureAuth(getSource))

View file

@ -3,12 +3,32 @@ package sync
import ( import (
"net/http" "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/arcgis"
"github.com/Gleipnir-Technology/nidus-sync/db"
"github.com/Gleipnir-Technology/nidus-sync/db/models" "github.com/Gleipnir-Technology/nidus-sync/db/models"
"github.com/Gleipnir-Technology/nidus-sync/html" "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 ArcGISOAuth *models.OauthToken
URL ContentURL URL ContentURL
User User User User
@ -26,6 +46,42 @@ func getSetting(w http.ResponseWriter, r *http.Request, u *models.User) {
} }
html.RenderOrError(w, "sync/settings.html", data) 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) { func getSettingIntegration(w http.ResponseWriter, r *http.Request, u *models.User) {
ctx := r.Context() ctx := r.Context()
userContent, err := contentForUser(ctx, u) 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) respondError(w, "Failed to get oauth", err, http.StatusInternalServerError)
return return
} }
data := ContentSettingIntegration{ data := contentSettingIntegration{
ArcGISOAuth: oauth, ArcGISOAuth: oauth,
URL: newContentURL(), URL: newContentURL(),
User: userContent, User: userContent,

View file

@ -9,6 +9,7 @@ type ContentURL struct {
PoolCSVUpload string PoolCSVUpload string
SamplePoolCSV string SamplePoolCSV string
Setting string Setting string
SettingDistrict string
SettingIntegration string SettingIntegration string
SettingPesticide string SettingPesticide string
SettingPesticideAdd string SettingPesticideAdd string
@ -23,6 +24,7 @@ func newContentURL() ContentURL {
PoolCSVUpload: config.MakeURLNidus("/pool/upload"), PoolCSVUpload: config.MakeURLNidus("/pool/upload"),
SamplePoolCSV: config.MakeURLNidus("/static/file/sample-pool.csv"), SamplePoolCSV: config.MakeURLNidus("/static/file/sample-pool.csv"),
Setting: config.MakeURLNidus("/setting"), Setting: config.MakeURLNidus("/setting"),
SettingDistrict: config.MakeURLNidus("/setting/district"),
SettingIntegration: config.MakeURLNidus("/setting/integration"), SettingIntegration: config.MakeURLNidus("/setting/integration"),
SettingPesticide: config.MakeURLNidus("/setting/pesticide"), SettingPesticide: config.MakeURLNidus("/setting/pesticide"),
SettingPesticideAdd: config.MakeURLNidus("/setting/pesticide/add"), SettingPesticideAdd: config.MakeURLNidus("/setting/pesticide/add"),

View file

@ -18,3 +18,5 @@ GRANT SELECT ON publicreport.report_location TO "tegola";
GRANT ALL PRIVILEGES ON SCHEMA public TO $1; GRANT ALL PRIVILEGES ON SCHEMA public TO $1;
-- do import of district data -- 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 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;