@kahwee/sf-map-svg 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,117 @@
1
+ export type Position = readonly [number, number, ...number[]];
2
+ export type Bounds = readonly [number, number, number, number];
3
+ export type Geometry =
4
+ | { readonly type: 'Point'; readonly coordinates: Position }
5
+ | { readonly type: 'LineString' | 'MultiPoint'; readonly coordinates: readonly Position[] }
6
+ | {
7
+ readonly type: 'Polygon' | 'MultiLineString';
8
+ readonly coordinates: readonly (readonly Position[])[];
9
+ }
10
+ | {
11
+ readonly type: 'MultiPolygon';
12
+ readonly coordinates: readonly (readonly (readonly Position[])[])[];
13
+ }
14
+ | { readonly type: 'GeometryCollection'; readonly geometries: readonly Geometry[] };
15
+ export interface Feature<P = Readonly<Record<string, unknown>>> {
16
+ readonly type: 'Feature';
17
+ readonly id: string;
18
+ readonly bbox: Bounds;
19
+ readonly properties: P;
20
+ readonly geometry: Geometry;
21
+ }
22
+ export interface DataSource {
23
+ readonly id: string;
24
+ readonly title: string;
25
+ readonly url: string;
26
+ readonly retrievedAt: string;
27
+ readonly definitionYear?: number;
28
+ readonly licenseUrl: string;
29
+ }
30
+ export interface Definition {
31
+ readonly kind: string;
32
+ readonly year?: number;
33
+ readonly description: string;
34
+ }
35
+ export interface FeatureCollection<P = Readonly<Record<string, unknown>>> {
36
+ readonly type: 'FeatureCollection';
37
+ readonly schemaVersion: 1;
38
+ readonly id: string;
39
+ readonly title: string;
40
+ readonly coordinateSystem: string;
41
+ readonly definition: Definition;
42
+ readonly sources: readonly DataSource[];
43
+ readonly topology?: {
44
+ readonly policy: 'disjoint-interiors';
45
+ readonly sharedBoundariesAllowed: boolean;
46
+ readonly method: string;
47
+ readonly tool: string;
48
+ readonly processedAt: string;
49
+ readonly sourceOverlapPairs: number;
50
+ };
51
+ readonly features: readonly Feature<P>[];
52
+ }
53
+ export type NeighborhoodSource = 'sf-find' | 'analysis' | 'realtor';
54
+ export interface NeighborhoodProperties {
55
+ readonly name: string;
56
+ readonly canonicalName: string;
57
+ readonly sourceName: string;
58
+ readonly aliases: readonly string[];
59
+ readonly definitionSource: NeighborhoodSource;
60
+ readonly nameSources: readonly string[];
61
+ readonly note?: string;
62
+ readonly sourceCode?: string;
63
+ readonly realtorDistrict?: string;
64
+ }
65
+ export interface NeighborhoodEntry extends NeighborhoodProperties {
66
+ readonly id: string;
67
+ readonly source: NeighborhoodSource;
68
+ readonly file: string;
69
+ readonly bbox: Bounds;
70
+ }
71
+ export interface DistrictProperties {
72
+ readonly district: number;
73
+ readonly name: string;
74
+ readonly year: 2002 | 2012 | 2022;
75
+ readonly label: Position;
76
+ readonly labelPoints: readonly Position[];
77
+ readonly displayExtras: Geometry | null;
78
+ }
79
+ export interface Catalog {
80
+ readonly schemaVersion: 1;
81
+ readonly description: string;
82
+ readonly datasets: readonly {
83
+ readonly id: string;
84
+ readonly file: string;
85
+ readonly title: string;
86
+ readonly featureCount: number;
87
+ readonly definition: Definition;
88
+ readonly sources: readonly DataSource[];
89
+ }[];
90
+ readonly neighborhoods: readonly NeighborhoodEntry[];
91
+ }
92
+ export declare const coast: FeatureCollection<{ readonly name: string }>;
93
+ export declare const districtMaps: Readonly<
94
+ Record<2002 | 2012 | 2022, FeatureCollection<DistrictProperties>>
95
+ >;
96
+ export declare const neighborhoods: FeatureCollection<NeighborhoodProperties>;
97
+ export declare const neighborhoodCollections: Readonly<
98
+ Record<NeighborhoodSource, FeatureCollection<NeighborhoodProperties>>
99
+ >;
100
+ export declare const neighborhoodSources: readonly NeighborhoodSource[];
101
+ export declare const highways: FeatureCollection<{ readonly route: string }>;
102
+ export declare const landmarks: FeatureCollection<{
103
+ readonly name: string;
104
+ readonly label: Position;
105
+ readonly offset: readonly [number, number];
106
+ readonly anchor: 'middle' | 'start' | 'end';
107
+ }>;
108
+ export declare const bartStations: FeatureCollection<{ readonly name: string }>;
109
+ export declare const catalog: Catalog;
110
+ export declare function getNeighborhood(
111
+ name: string,
112
+ options?: { source?: NeighborhoodSource },
113
+ ): Feature<NeighborhoodProperties> | undefined;
114
+ export declare function searchNeighborhoods(
115
+ query?: string,
116
+ options?: { source?: NeighborhoodSource },
117
+ ): NeighborhoodEntry[];
package/data/index.js ADDED
@@ -0,0 +1,69 @@
1
+ import coastData from './coast.json' with { type: 'json' };
2
+ import districts2002 from './districts-2002.json' with { type: 'json' };
3
+ import districts2012 from './districts-2012.json' with { type: 'json' };
4
+ import districts2022 from './districts-2022.json' with { type: 'json' };
5
+ import sfFind from './neighborhoods.json' with { type: 'json' };
6
+ import analysis from './neighborhoods-analysis.json' with { type: 'json' };
7
+ import realtor from './neighborhoods-realtor.json' with { type: 'json' };
8
+ import highwayData from './highways.json' with { type: 'json' };
9
+ import landmarkData from './landmarks.json' with { type: 'json' };
10
+ import stationData from './bart-stations.json' with { type: 'json' };
11
+ import catalogData from './catalog.json' with { type: 'json' };
12
+ import { deepFreeze } from '../src/immutable.js';
13
+
14
+ export const coast = deepFreeze(coastData);
15
+ export const districtMaps = deepFreeze({
16
+ 2002: districts2002,
17
+ 2012: districts2012,
18
+ 2022: districts2022,
19
+ });
20
+ export const neighborhoods = deepFreeze(realtor);
21
+ export const neighborhoodCollections = deepFreeze({ 'sf-find': sfFind, analysis, realtor });
22
+ export const neighborhoodSources = Object.freeze(Object.keys(neighborhoodCollections));
23
+ export const highways = deepFreeze(highwayData);
24
+ export const landmarks = deepFreeze(landmarkData);
25
+ export const bartStations = deepFreeze(stationData);
26
+ export const catalog = deepFreeze(catalogData);
27
+
28
+ const normalize = (value) =>
29
+ value
30
+ .normalize('NFKD')
31
+ .toLowerCase()
32
+ .replace(/[\u0300-\u036f'’]/g, '')
33
+ .replace(/[^a-z0-9]+/g, ' ')
34
+ .trim();
35
+ function checkSource(source) {
36
+ if (!Object.hasOwn(neighborhoodCollections, source))
37
+ throw new RangeError(`Unknown neighborhood source: ${source}`);
38
+ }
39
+ const names = (feature) => [
40
+ feature.id,
41
+ feature.properties.canonicalName,
42
+ feature.properties.sourceName,
43
+ ...feature.properties.aliases,
44
+ ];
45
+
46
+ /** Exact ID, canonical name, source name, or alias lookup within one definition set. */
47
+ export function getNeighborhood(name, { source = neighborhoods.id } = {}) {
48
+ checkSource(source);
49
+ if (typeof name !== 'string') throw new TypeError('Neighborhood name must be a string.');
50
+ const query = normalize(name);
51
+ if (!query) return undefined;
52
+ return neighborhoodCollections[source].features.find((feature) =>
53
+ names(feature).some((value) => normalize(value) === query),
54
+ );
55
+ }
56
+
57
+ /** Search names across sources; results keep their source identity and omit geometry. */
58
+ export function searchNeighborhoods(query = '', { source } = {}) {
59
+ if (source !== undefined) checkSource(source);
60
+ if (typeof query !== 'string') throw new TypeError('Neighborhood query must be a string.');
61
+ const term = normalize(query);
62
+ return catalog.neighborhoods.filter(
63
+ (entry) =>
64
+ (source === undefined || entry.source === source) &&
65
+ [entry.id, entry.canonicalName, entry.sourceName, ...entry.aliases].some((value) =>
66
+ normalize(value).includes(term),
67
+ ),
68
+ );
69
+ }