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 |
Layer Calendar & Time · Distribution & Publishing Platform Microsoft Platform Module Reservations · 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.
- Address database
- Parcel polygons
- Building footprints
- Floor plans (select buildings)
component
at entry time
- 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.
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.
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
| Field | Type | Purpose |
|---|---|---|
facilityId | int FK | Parent Facility. The only structural relationship — spaces do not nest. |
name | string | Canonical name. Stable reference for Programs, Reservations, and Calendar. |
nameOverride | string? | Optional public-facing display name. Null = use name. |
description | string? | Optional public-facing description for citizen-visible pages. |
capacity | int? | Physical capacity. Operational property — unchanged by display decisions. |
floor, addressUnit | string? | Physical location descriptors within the Facility. |
spaceType | enum | Semantic classification of what the space is functionally: room | area | desk | court | other. |
geometryType | enum | Drawing layer role — how the geometry is rendered on a map: walls | zone | object | overlay. See below. |
location | polygon? | Spatial geometry. SRID 102629 (state plane). May be null for spaces with no drawn boundary. |
calendarId | int? FK | Optional FK to data.Calendar. Present when the space participates in time-based activity — programs, citizen reservations, or both. See Calendar & Time. |
exchangeResourceMailbox | string? | 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. |
reservationConfig | JSON? | 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).
| Value | Represents | Render layer |
|---|---|---|
walls | Architectural structure — room and corridor boundaries, typically from floor plan import | Base |
zone | A defined area or region; may cross room boundaries (e.g. an event area, an outdoor section) | Above walls |
object | A physical item within a space — furniture, shelving, equipment | Above zones |
overlay | Contextual annotation — a highlighted region, a temporary area marker | Top |
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.
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/capacity → nameOverride/description/capacity on FacilitySpace; bookingRules JSON → FacilitySpace.reservationConfig; isReservable flag → null check on reservationConfig; isPublic → Calendar.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 (reservationConfigJSON — 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
| Option | FK to | Why 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:
-
exchangeResourceMailboxis 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 separateFacilitySpaceReservationConfigentity is needed.