Auburn MyGov Docs Gitea

Layer

Location

How physical location is modeled, stored, and referenced across MyGov — the spatial authority boundary, the flat Facility → FacilitySpace model, and how every record that happens somewhere resolves its location.

Status Draft
Last updated
Related LayerCalendar & Time · Distribution & Publishing
PlatformMicrosoft Platform
ModuleReservations · Programs & Registration
UI Components parks-facility-recreation-list / -links — facility directory, grouped by category; tied to FacilityContact
facility-space-booking — citizen-facing reservation wizard (space selection → availability → form → confirm/cancel)

Spatial Authority and the ArcGIS Boundary

The City of Auburn maintains its own address and parcel database, served through an ArcGIS portal. For MyGov's purposes this system is treated as a third-party service — authoritative for spatial data but external to the MyGov API boundary. MyGov does not query ArcGIS at runtime. Instead:

  • At data entry time, custom web components in the admin portal call the ArcGIS portal REST API directly from the browser to look up addresses and parcels. The selected result is what gets stored in MyGov.
  • At rest, MyGov stores what it needs locally on the Facility record: a normalized address string, a point coordinate (geometry, state plane projection), and optionally a parcel ID for reference back to the authoritative source.
  • ArcGIS also holds building and floor plan data for some city facilities, including room-level geometry. Where this data exists and aligns with a FacilitySpace (e.g. a conference room that also has an Exchange resource mailbox), it can be imported to seed FacilitySpace records. It is a starting point, not a live dependency.
External — spatial authority
ArcGIS Portal
  • Address database
  • Parcel polygons
  • Building footprints
  • Floor plans (select buildings)
Browser
component
at entry time
Internal — MyGov
Facility Record
  • Normalized address (string)
  • Point geometry (state plane)
  • Parcel ID (reference only)
  • User-defined FacilitySpaces

Coordinates are stored as SQL geometry in state plane projection (consistent with the city's GIS environment) rather than WGS 84 geography. Any spatial queries — distance calculations, containment checks — must account for this projection. Conversion to WGS 84 / lat-lng happens at the API or presentation layer when needed for web mapping components.

Facility and FacilitySpace — The Physical Anchor

Facility is the top-level physical entity: a building, park, or site the city owns or manages. It carries the authoritative spatial data (address, geometry) and the public-facing contact record (FacilityContact — see Distribution & Publishing).

FacilitySpace is any named, bounded area within a Facility that is meaningful to track independently — a room, a court, a desk, a collection of shelves, a defined outdoor area. A FacilitySpace does not need to be reservable; it may simply exist as a spatial reference that other records (programs, employees, library collections) can point to.

FacilitySpaces have two origins:

  • ArcGIS import — rooms with surveyed geometry from the city's floor plan data. These may carry a geometry footprint and align with Exchange resource mailboxes. Import is a one-time or periodic operation, not a live sync.
  • User-defined — operational spaces staff create in the admin portal. A basketball half-court, a study carrel, a section of outdoor field. These may have no geometry beyond their parent Facility's coordinates, or staff may draw a rough boundary.

Both origins produce the same FacilitySpace record type. The data model makes no distinction — origin is metadata, not structure.

The Flat Structure — Facility → FacilitySpace

FacilitySpaces belong to a Facility. That is the only structural relationship between them. Spaces do not nest, do not contain other spaces, and have no awareness of each other in the data model.

Spaces may overlap physically in the real world — a desk sits inside a room, a study carrel occupies a corner of a larger space — but the data model does not encode that. Physical co-location is a real-world fact; it is not a data relationship. A desk is a FacilitySpace. The room it sits in is a FacilitySpace. Both belong to the same Facility. Neither knows about the other.

Facility
Auburn Public Library — Main Branch
FacilitySpace
Youth Room
Program Calendar Event
FacilitySpace
Study Desk 1
Reservable
FacilitySpace
Study Desk 2
Reservable
FacilitySpace
Reference Section
Employee Collection
FacilitySpace
Meeting Room A
Reservable Calendar Event
Overlap is a reservation concern, not a location concern. The fact that Study Desk 1 physically sits inside the Youth Room is not modeled here. Whether a Youth Room program booking should block a desk reservation, or whether a basketball full-court booking conflicts with two half-court bookings, are reservation conflict rules — handled in Reservations. The location model only needs to know that all of these spaces belong to the same Facility.

How Records Reference Location

Any record that happens somewhere carries a location reference. There are two reference types:

Type How it works What resolves from it Used when
Structured A foreign key to a FacilitySpace or Facility record Space name, parent facility name, address, geometry, hours, contact card, calendar, containment context The location is a city-owned or managed facility. Programs, reservations, most calendar events, employee workspaces.
Unstructured A free-text location name and/or address string, optional manual coordinates Display text only. No facility page link, no calendar cross-reference, no containment context. Off-inventory locations: university facilities, external venues, temporary or one-time sites not in the Facility records.

A record may carry both — a structured FacilitySpaceId and a free-text location override — where the free-text is used for display (e.g. "Rec Center, East Gym — enter via Oak Street") while the structured ID drives calendar association, availability, and cross-referencing. When both are present the structured ID is authoritative for system behavior; the free-text is for human readability only.

Location propagation — no redundant storage

A record that references a FacilitySpaceId never needs to store the address, coordinates, or facility name separately. Those are resolved by a simple two-hop lookup at query or display time:

  • FacilitySpace → parent Facility → address, geometry, contact card, hours

Calendar entries (Option B from Calendar Aggregation) carry a denormalized location text field for iCal export compatibility. This text is derived from the structured reference at write time — not entered manually when a FacilitySpaceId is present — and is refreshed whenever the referenced space or facility record changes.

"Everything at This Location"

The containment model makes it possible to answer the question "what is associated with this Facility or FacilitySpace?" across all modules. This is the connective tissue that turns location from a display field into an integration layer.

Given a Facility, the system can enumerate:

  • All FacilitySpaces belonging to it
  • All Calendar Events and Program Sessions with a structured reference to this Facility or any of its spaces
  • All Reservations (confirmed, pending) for any of its spaces
  • All Employees whose workspace references this Facility or one of its spaces
  • The Facility's calendar (aggregated view of events across all its spaces)
  • The FacilityContact card shown on public pages

Given a FacilitySpace, the same query narrows to that space only — no sub-space traversal needed. A study desk query returns reservations for that desk. A room query returns programs, events, and reservations referencing that room directly.

In scope for this design, deferred for presentation. The connections documented here are what make a "facility dashboard" or "everything at this address" citizen view possible. The design of that view — whether it is a page, a panel, a map, or an API endpoint — is deferred. What this document establishes is that the data model supports traversal; how it is surfaced is a UI/API decision for a later phase.

FacilitySpace — Single Entity

FacilitySpace is a single entity. It carries the spatial identity of the space, the fields for operational use (reservations, programs, calendar association), and the fields for public display. There is no separate FacilitySpaceContact split.

Why no Entity/Contact split

The Facility/FacilityContact split exists because Facility geometry is GIS-authoritative — imported from an external system — and because the same building may be presented as different named entities by different departments. Neither condition applies to FacilitySpaces:

  • CMS editors create FacilitySpace geometry — they draw boundaries for rooms and areas alongside GIS-imported wall data. There is no authoritative external layer to protect. The geometry staff draws is the geometry; it is not overriding a separate authoritative source the way FacilityContact overrides GIS-sourced Facility data.
  • A FacilitySpace has one identity. A room does not present as a different named entity to different departments the way a building can.

A nameOverride and description field on FacilitySpace itself are sufficient for public display customisation. A separate Contact entity adds schema complexity with no corresponding benefit.

Current state: cms.FacilitySpaceContact exists in the codebase as a separate entity. Its fields are being folded into organization.FacilitySpace directly. The migration is tracked in the audit section below.

Key fields

FieldTypePurpose
facilityIdint FK Parent Facility. The only structural relationship — spaces do not nest.
namestring Canonical name. Stable reference for Programs, Reservations, and Calendar.
nameOverridestring? Optional public-facing display name. Null = use name.
descriptionstring? Optional public-facing description for citizen-visible pages.
capacityint? Physical capacity. Operational property — unchanged by display decisions.
floor, addressUnitstring? Physical location descriptors within the Facility.
spaceTypeenum Semantic classification of what the space is functionally: room | area | desk | court | other.
geometryTypeenum Drawing layer role — how the geometry is rendered on a map: walls | zone | object | overlay. See below.
locationpolygon? Spatial geometry. SRID 102629 (state plane). May be null for spaces with no drawn boundary.
calendarIdint? FK Optional FK to data.Calendar. Present when the space participates in time-based activity — programs, citizen reservations, or both. See Calendar & Time.
exchangeResourceMailboxstring? Exchange room resource mailbox address (e.g. confroom-a@auburnal.gov). Present for any space that has an Exchange room resource — whether or not it is citizen-reservable through MyGov. Used for inventory tracking and for Place-level Graph API operations. Distinct from the Calendar's exchangeCalendarAddress. See Microsoft Platform.
reservationConfigJSON? Null = not citizen-reservable. When set, the space is configured for citizen booking; the JSON schema (API-enforced) defines the booking policy. Replaces both the separate isReservable flag and the former FacilitySpaceReservationConfig entity. See Reservations.

geometryType — drawing layers

geometryType describes what the geometry represents visually and therefore which rendering layer it belongs to. It is not about data provenance or edit permissions — it is about how multiple geometries coexist on the same map (GIS-imported walls, staff-drawn zones, and placed objects all live in the same flat FacilitySpace list but render at different layers).

ValueRepresentsRender layer
wallsArchitectural structure — room and corridor boundaries, typically from floor plan importBase
zoneA defined area or region; may cross room boundaries (e.g. an event area, an outdoor section)Above walls
objectA physical item within a space — furniture, shelving, equipmentAbove zones
overlayContextual annotation — a highlighted region, a temporary area markerTop

spaceType and geometryType are independent. A basketball court is spaceType: court, geometryType: zone. A bookcase is spaceType: area, geometryType: object. Both sit in the same FacilitySpace table; only geometryType determines how they are layered on the map.

reservationConfig — booking policy as a JSON field

Rather than a separate reservation config entity, booking policy is a nullable JSON field on FacilitySpace. The API defines and validates the schema; the database stores it as a typed JSON column. Its presence is the reservability signal — no separate isReservable boolean is needed.

The JSON schema covers: approval flow, occupancy model (sole-use, occupancy-limited, divisions), operating hours, max duration, lead time, buffer between bookings, booking window, cost/payment notice, required form fields, and staff notification settings. Full schema in Reservations.

Overlapping reservations — deferred. Whether physically co-located spaces (e.g. a room and a desk inside it) share reservation conflict logic is an open question in Reservations. The location model does not prejudge that decision.

Audit Findings

Codebase audited: api/Models/organization/Facility.cs, api/Models/organization/FacilitySpace.cs, api/Models/CMS/FacilityContact.cs, api/Models/CMS/FacilitySpaceContact.cs

Schema — as built

Model Table Key fields Notes
Facility organization.Facility name, samsaraId, gisFacilityId, address, zipCode, point (geometry, SRID 102629), location (polygon, SRID 102629), latLongPoint (deprecated) Spatial data stored in state plane projection (102629) via NetTopologySuite. LatLongPoint (SRID 4326) is marked [Obsolete] — use Point with ProjNet transformation instead.
FacilityContact cms.FacilityContact facilityId? (nullable FK), gisFacilityId, nameSource, addressSource, nameOverride, addressOverride, suite, phone, email, hours (JSON), imagePath, useType, calendarUrl, reservationUrl, isPublished, lastSync Seed-and-override implemented at DB level via computed columns: Name = COALESCE(NameOverride, NameSource). FacilityId is nullable — FacilityContacts can exist without a linked organization.Facility.
FacilitySpace organization.FacilitySpace facilityId (FK), name, floor, addressUnit, exchangeResourceMailbox (nullable SMTP address — correctly located here), gisObjectId, gisFacilityId, location (polygon, SRID 102629) Flat model confirmed — no parent space FK. Has polygon geometry. Design targets not yet present: calendarId FK, nameOverride, description, spaceType enum, geometryType enum, reservationConfig JSON field.
FacilitySpaceContact cms.FacilitySpaceContact facilitySpaceId (unique FK), name, description, capacity, isReservable, isPublic, bookingRules (JSON), imagePath, data (JSON) Collapse target. FacilitySpaceContact is being folded into FacilitySpace as direct fields. Migration: name/description/capacitynameOverride/description/capacity on FacilitySpace; bookingRules JSON → FacilitySpace.reservationConfig; isReservable flag → null check on reservationConfig; isPublicCalendar.isPublic.

Feature status

Feature Status Notes
Facility entity with spatial geometry BUILT Point + Polygon both present, state plane 102629
FacilityContact seed-and-override pattern BUILT Computed columns at DB level; FacilityId is nullable for non-GIS facilities
FacilitySpace flat model (no nesting) BUILT Confirmed: FacilityId FK only, no parent space
FacilitySpaceContact collapse → FacilitySpace fields NOT STARTED Fold cms.FacilitySpaceContact fields into organization.FacilitySpace directly. Add nameOverride, description to FacilitySpace; migrate bookingRules JSON → reservationConfig JSON field (see below).
FacilitySpace.calendarId FK NOT STARTED Optional FK to data.Calendar not yet present on FacilitySpace. Required for Calendar-as-broker model and for the "everything at this location" query.
FacilitySpace.spaceType + geometryType enums NOT STARTED spaceType (room | area | desk | court | other) and geometryType (walls | zone | object | overlay) not yet present. geometryType is needed to correctly layer multiple geometry types on the map.
FacilitySpace.reservationConfig JSON field NOT STARTED Nullable JSON column replacing both the isReservable flag (null = not reservable) and the former FacilitySpaceReservationConfig entity approach. Schema defined and validated at the API layer. Migrate existing bookingRules JSON from FacilitySpaceContact. Tracking in Reservations.
ArcGIS import for space boundaries PARTIAL GisObjectId and GisFacilityId are on FacilitySpace; Location polygon is the field. No import workflow audited yet. Implementation questions: which facilities have usable floor plan data, one-time seed vs. periodic sync, admin portal tooling vs. script — resolve during build.
Coordinate projection at API boundary NOT DECIDED API stores state plane (SRID 102629). Decide during web mapping component build whether conversion to WGS 84 happens at the API response layer or in the client. Must be consistent across all map-rendering components.
FacilityContact.CalendarUrl / ReservationUrl BUILT ICS calendar link and reservation page link stored on FacilityContact for use in public web components
Deprecated LatLongPoint cleanup PENDING Marked [Obsolete] in code; column still in schema. Should be dropped once consumers verified to use Point+ProjNet.

Canonical Location Reference — Which Foreign Key?

Programs, Events, and Reservations each need to reference "where they happen." The right answer depends on asking what each candidate entity actually is — not which entity happens to carry a convenient field.

What each entity represents

Working from first principles, FacilitySpace is a single entity covering spatial identity, display, and booking configuration:

  • organization.FacilitySpace — the complete space record. Spatial identity (GIS identifiers, floor, unit, polygon geometry), public display fields (nameOverride, description), Exchange room resource association (exchangeResourceMailbox), time-layer association (calendarId), and booking policy (reservationConfig JSON — null if not citizen-reservable).

The former cms.FacilitySpaceContact split has been removed from the design. Its fields are being migrated directly to FacilitySpace. See the Audit Findings section for current migration status.

Decision: use FacilitySpaceId

Programs, Events, and Reservations carry a FacilitySpaceId FK to organization.FacilitySpace because FacilitySpace is the stable spatial identity of the thing being referenced. A program happens at a space — not at how a space is currently displayed, and not at the space's current booking policy. Those are joined in as needed. At the facility level the same rule applies: where a record needs only a building anchor (no specific space), it carries FacilityId.

Options considered

OptionFK toWhy not chosen
FacilitySpaceId Chosen organization.FacilitySpace This is the stable spatial identity. Display and booking policy are both joined in when needed — neither should be the anchor for a record about scheduling and location.
FacilityId only organization.Facility Loses space-level granularity — can't distinguish "Library, Study Room 1" from "Library, Computer Lab." Not viable for conflict detection or Exchange blocking.

Where do exchangeResourceMailbox and booking policy belong?

Both belong directly on FacilitySpace:

  • exchangeResourceMailbox is an inventory property of the space — "this room has an Exchange room resource at this address." It is true whether or not the space is citizen-reservable through MyGov. A staff-only conference room that is booked entirely through Outlook still has a resource mailbox, and that association should be visible on the space record without requiring a Calendar or reservation configuration to exist first.
  • reservationConfig (JSON) is the booking policy. A nullable JSON field on FacilitySpace is the right scope — the config is a scalar blob that belongs to one space, is not referenced by other entities, and does not need individual fields to be queryable at the database level. Its presence is the reservability signal; null means not citizen-reservable. No separate FacilitySpaceReservationConfig entity is needed.