refactor(B5): shared geometry module factory for mun/TI (F3)

Extract buildGeometryModuleFromTopology to collapse the duplicate
topojson.feature → features → optional Map pipeline in municipality
and territory geometry submodules. F2 (ensureCachedDownload) was
already delivered in scripts/lib/cli.mjs via P3-H.

Closes #28
This commit is contained in:
Cursor Agent 2026-07-31 01:38:03 +00:00
parent e2bc366c10
commit ce67738485
3 changed files with 99 additions and 19 deletions

View file

@ -0,0 +1,85 @@
import { feature } from 'topojson-client'
import type { Feature, MultiPolygon, Polygon } from 'geojson'
import type { GeometryCollection, Topology } from 'topojson-specification'
type PolygonalFeature<Properties extends Record<string, unknown>> = Feature<
Polygon | MultiPolygon,
Properties
>
type GeometryModuleBase<
Properties extends Record<string, unknown>,
TObjectName extends string,
> = {
topology: Topology<{ [key in TObjectName]: GeometryCollection<Properties> }>
features: readonly PolygonalFeature<Properties>[]
}
type IndexedGeometryModule<
Properties extends Record<string, unknown>,
TObjectName extends string,
KeyProperty extends keyof Properties & string,
> = GeometryModuleBase<Properties, TObjectName> & {
getFeatureByKey: (key: Properties[KeyProperty]) => PolygonalFeature<Properties> | undefined
}
type BuildGeometryModuleOptions<
Properties extends Record<string, unknown>,
TObjectName extends string,
KeyProperty extends keyof Properties & string,
> = {
topology: Topology<{ [key in TObjectName]: GeometryCollection<Properties> }>
objectName: TObjectName
keyProperty?: KeyProperty
}
export function buildGeometryModuleFromTopology<
Properties extends Record<string, unknown>,
TObjectName extends string,
KeyProperty extends keyof Properties & string,
>(
options: BuildGeometryModuleOptions<Properties, TObjectName, KeyProperty> & {
keyProperty: KeyProperty
},
): IndexedGeometryModule<Properties, TObjectName, KeyProperty>
export function buildGeometryModuleFromTopology<
Properties extends Record<string, unknown>,
TObjectName extends string,
>(
options: BuildGeometryModuleOptions<Properties, TObjectName, never>,
): GeometryModuleBase<Properties, TObjectName>
export function buildGeometryModuleFromTopology<
Properties extends Record<string, unknown>,
TObjectName extends string,
KeyProperty extends keyof Properties & string,
>({
topology,
objectName,
keyProperty,
}: BuildGeometryModuleOptions<Properties, TObjectName, KeyProperty>):
| GeometryModuleBase<Properties, TObjectName>
| IndexedGeometryModule<Properties, TObjectName, KeyProperty> {
const features = feature(topology, topology.objects[objectName])
.features as PolygonalFeature<Properties>[]
const base: GeometryModuleBase<Properties, TObjectName> = {
topology,
features,
}
if (!keyProperty) {
return base
}
const featuresByKey = new Map(
features.map((entry) => [entry.properties[keyProperty], entry]),
)
return {
...base,
getFeatureByKey: (key) => featuresByKey.get(key),
}
}

View file

@ -1,23 +1,19 @@
import { feature } from 'topojson-client'
import type {
BahiaMunicipalityFeature,
MunicipalityGeometryModule,
MunicipalityTopology,
} from '@/lib/bahiaGeometriesTypes'
import { buildGeometryModuleFromTopology } from '@/lib/bahiaGeometryModuleFactory'
import municipalityTopologyJson from '@/lib/geometries/bahia-municipalities.topo.json'
const topology = municipalityTopologyJson as unknown as MunicipalityTopology
const municipalityFeatures = feature(topology, topology.objects.municipalities)
.features as BahiaMunicipalityFeature[]
const municipalityByCodarea = new Map(
municipalityFeatures.map((entry) => [entry.properties.codarea, entry]),
)
const { topology, features, getFeatureByKey } = buildGeometryModuleFromTopology({
topology: municipalityTopologyJson as unknown as MunicipalityTopology,
objectName: 'municipalities',
keyProperty: 'codarea',
})
export const municipalityGeometryModule: MunicipalityGeometryModule = {
topology,
features: municipalityFeatures,
getMunicipalityFeature: (codarea: string) => municipalityByCodarea.get(codarea),
features: features as readonly BahiaMunicipalityFeature[],
getMunicipalityFeature: getFeatureByKey,
}

View file

@ -1,18 +1,17 @@
import { feature } from 'topojson-client'
import type {
BahiaTerritoryFeature,
TerritoryGeometryModule,
TerritoryTopology,
} from '@/lib/bahiaGeometriesTypes'
import { buildGeometryModuleFromTopology } from '@/lib/bahiaGeometryModuleFactory'
import territoryTopologyJson from '@/lib/geometries/bahia-identity-territories.topo.json'
const topology = territoryTopologyJson as unknown as TerritoryTopology
const territoryFeatures = feature(topology, topology.objects.territories)
.features as BahiaTerritoryFeature[]
const { topology, features } = buildGeometryModuleFromTopology({
topology: territoryTopologyJson as unknown as TerritoryTopology,
objectName: 'territories',
})
export const territoryGeometryModule: TerritoryGeometryModule = {
topology,
features: territoryFeatures,
features: features as readonly BahiaTerritoryFeature[],
}