lance-ts 1.0.0 → 1.0.1

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.
package/README.md ADDED
@@ -0,0 +1,195 @@
1
+ # lance-ts
2
+
3
+ **Opinionated TypeScript client for the U.S. Census Geocoder API.**
4
+
5
+ Turn U.S. addresses into latitude/longitude coordinates using the free, no-key-required [Census Bureau Geocoder](https://geocoding.geo.census.gov/geocoder/) — with full TypeScript types and zero external HTTP dependencies.
6
+
7
+ ---
8
+
9
+ ## Use Case:
10
+
11
+ If you're working with U.S. address data, the Census Geocoder is an extremely viable, completely free API. Backed by the official MAF/TIGER address database, and covering virtually every U.S. address, it has no posted rate limits and no API key.
12
+
13
+ ### The Issue:
14
+
15
+ The raw API response is verbose and awkward to work with.
16
+ Below is the simplest possible response; a single one-line address match:
17
+ ```json
18
+ {"result": {
19
+ "input": {
20
+ "address": {"address": "4600 Silver Hill Rd, Washington, DC 20233"},
21
+ "benchmark": {
22
+ "isDefault": true,
23
+ "benchmarkDescription": "Public Address Ranges - Current Benchmark",
24
+ "id": "4",
25
+ "benchmarkName": "Public_AR_Current"
26
+ }
27
+ },
28
+ "addressMatches": [{
29
+ "tigerLine": {
30
+ "side": "L",
31
+ "tigerLineId": "76355984"
32
+ },
33
+ "coordinates": {
34
+ "x": -76.92748724230096,
35
+ "y": 38.84601622386617
36
+ },
37
+ "addressComponents": {
38
+ "zip": "20233",
39
+ "streetName": "SILVER HILL",
40
+ "preType": "",
41
+ "city": "WASHINGTON",
42
+ "preDirection": "",
43
+ "suffixDirection": "",
44
+ "fromAddress": "4600",
45
+ "state": "DC",
46
+ "suffixType": "RD",
47
+ "toAddress": "4700",
48
+ "suffixQualifier": "",
49
+ "preQualifier": ""
50
+ },
51
+ "matchedAddress": "4600 SILVER HILL RD, WASHINGTON, DC, 20233"
52
+ }]
53
+ }}
54
+ ```
55
+ For applications that require lightweight & simple coordinate responses, `lance-ts` provides a clean, strongly-typed interface so you can go from address → coordinates in one call.
56
+
57
+ - 🆓 **Free** — no API key, no account, no rate limits
58
+ - 🏛️ **Official Data** — calls the U.S. Census Bureau Geocoder
59
+ - 📦 **Lightweight** — no HTTP library dependencies; uses node-native `fetch`
60
+
61
+ ---
62
+
63
+ ## Installation
64
+
65
+ ```bash
66
+ # pnpm (recommended)
67
+ pnpm add lance-ts
68
+
69
+ # npm
70
+ npm install lance-ts
71
+
72
+ # yarn
73
+ yarn add lance-ts
74
+ ```
75
+
76
+ Requires **Node.js 18+** (for native `fetch`).
77
+
78
+ ---
79
+
80
+ ## Quick Start
81
+
82
+ ```ts
83
+ import { geocode } from "lance-ts";
84
+
85
+ const result = await geocodeOneLineAddress(
86
+ "1600 Pennsylvania Avenue NW, Washington, DC 20500"
87
+ );
88
+
89
+ if (result) {
90
+ console.log(result.coordinates.x); // 38.8987...
91
+ console.log(result.coordinates.y); // -77.0353...
92
+ console.log(result.matchedAddress); // "1600 Pennsylvania..."
93
+ }
94
+ ```
95
+
96
+ ---
97
+
98
+ ## API
99
+
100
+ ### `geocode(address: string): Promise<GeocodeResult | null>`
101
+
102
+ Geocodes a single one-line U.S. address. Returns `null` if no match is found.
103
+
104
+ ```ts
105
+ const result = await geocode("350 Fifth Avenue, New York, NY 10118");
106
+ // → { lat: 40.7484, lng: -73.9856, matchedAddress: "350 5TH AVE, NEW YORK, NY, 10118" }
107
+ ```
108
+
109
+ **Parameters**
110
+
111
+ | Name | Type | Description |
112
+ |-----------|----------|------------------------------------------|
113
+ | `address` | `string` | A U.S. address in one-line format |
114
+
115
+ **Returns:** `Promise<GeocodeResult | null>`
116
+
117
+ ---
118
+
119
+ ### `GeocodeResult`
120
+
121
+ ```ts
122
+ type GeocodeResult = {
123
+ lat: number;
124
+ lng: number;
125
+ matchedAddress: string;
126
+ };
127
+ ```
128
+
129
+ ---
130
+
131
+ ## How It Works
132
+
133
+ `lance-ts` sends your address to the Census Bureau's public REST endpoint:
134
+
135
+ ```
136
+ https://geocoding.geo.census.gov/geocoder/locations/onelineaddress
137
+ ```
138
+
139
+ The response is parsed and normalized — extracting the matched coordinates and canonical address from the Census Bureau's nested JSON structure — and returned as a clean, typed object.
140
+
141
+ ---
142
+
143
+ ## Development
144
+
145
+ ```bash
146
+ # Install dependencies
147
+ pnpm install
148
+
149
+ # Run tests
150
+ pnpm test
151
+
152
+ # Run tests with UI
153
+ pnpm test --ui
154
+ ```
155
+
156
+ ### Project Structure
157
+
158
+ ```
159
+ lance-ts/
160
+ ├── src/ # TypeScript source
161
+ ├── dist/ # Compiled output (generated)
162
+ ├── tsconfig.json
163
+ └── package.json
164
+ ```
165
+
166
+ This project uses a strict `tsconfig.json` setup including:
167
+
168
+ - `strict: true`
169
+ - `noUncheckedIndexedAccess: true`
170
+ - `exactOptionalPropertyTypes: true`
171
+ - `verbatimModuleSyntax: true`
172
+ - `moduleResolution: "nodenext"`
173
+
174
+ ---
175
+
176
+ ## Limitations
177
+
178
+ - U.S. addresses only
179
+ - Coordinate results are **interpolated** from TIGER address ranges, not rooftop-precise
180
+ - Batch geocoding (up to 10,000 addresses) is not yet implemented
181
+ - Reverse geocoding (coordinates → address) is not yet implemented
182
+
183
+ ---
184
+
185
+ ## Resources
186
+
187
+ - [Census Geocoder Web Interface](https://geocoding.geo.census.gov/geocoder/)
188
+ - [Census Geocoder API Docs](https://www.census.gov/programs-surveys/geography/technical-documentation/complete-technical-documentation/census-geocoder.html)
189
+ - [MAF/TIGER Database Overview](https://www.census.gov/geographies/mapping-files/time-series/geo/tiger-line-file.html)
190
+
191
+ ---
192
+
193
+ ## License
194
+
195
+ ISC © [matt-the-thew](https://github.com/matt-the-thew)
package/dist/index.cjs ADDED
@@ -0,0 +1,162 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/index.ts
31
+ var index_exports = {};
32
+ __export(index_exports, {
33
+ BatchAddressGeocoder: () => BatchAddressGeocoder,
34
+ geocodeOneLineAddress: () => geocodeOneLineAddress,
35
+ geocodeOneLineAddressAll: () => geocodeOneLineAddressAll
36
+ });
37
+ module.exports = __toCommonJS(index_exports);
38
+
39
+ // src/one-line-address.ts
40
+ async function geocodeOneLineAddress(address) {
41
+ const params = new URLSearchParams({
42
+ address,
43
+ benchmark: "Public_AR_Current",
44
+ format: "json"
45
+ });
46
+ const url = `https://geocoding.geo.census.gov/geocoder/locations/onelineaddress?${params}`;
47
+ const response = await fetch(url);
48
+ if (!response.ok) throw new Error(`HTTP error: ${response.status}`);
49
+ const data = await response.json();
50
+ const matches = data.result?.addressMatches;
51
+ if (!matches?.length) {
52
+ console.log(`No matches found for: ${address}`);
53
+ return;
54
+ }
55
+ if (matches?.length > 1) {
56
+ console.warn(`Multiple address matches found for ${address}`);
57
+ for (const match of matches) {
58
+ console.warn(`[WARNING}: multimple matched addresses for: ${match.matchedAddress}
59
+ Coordinates:
60
+ X: ${match.coordinates["x"]}
61
+ Y: ${match.coordinates["y"]}
62
+ Consider geocodeOneLineAddressAll()`);
63
+ }
64
+ }
65
+ if (matches[0]) {
66
+ return {
67
+ coordinates: matches[0].coordinates,
68
+ matchedAddress: matches[0].matchedAddress
69
+ };
70
+ }
71
+ }
72
+ async function geocodeOneLineAddressAll(address) {
73
+ const params = new URLSearchParams({
74
+ address,
75
+ benchmark: "Public_AR_Current",
76
+ format: "json"
77
+ });
78
+ const url = `https://geocoding.geo.census.gov/geocoder/locations/onelineaddress?${params}`;
79
+ const response = await fetch(url);
80
+ if (!response.ok) throw new Error(`HTTP error: ${response.status}`);
81
+ const data = await response.json();
82
+ const matches = data.result?.addressMatches;
83
+ if (!matches?.length) {
84
+ console.log(`No matches found for: ${address}`);
85
+ return;
86
+ }
87
+ return matches;
88
+ }
89
+
90
+ // src/batch-address.ts
91
+ var fs = __toESM(require("fs"), 1);
92
+ var path = __toESM(require("path"), 1);
93
+ var import_meta = {};
94
+ var BatchAddressGeocoder = class {
95
+ // Set geocoder endpoint
96
+ url = "https://geocoding.geo.census.gov/geocoder/locations/addressbatch";
97
+ inputFilePath;
98
+ outputFilePath = "batch_coordinates.csv";
99
+ constructor(inputFilePath, outputFilePath) {
100
+ this.inputFilePath = inputFilePath;
101
+ if (outputFilePath) this.outputFilePath = outputFilePath;
102
+ }
103
+ /**
104
+ * Finds relative path for a file name
105
+ * @param {string} localFilePath
106
+ * @returns {string | undefined} the realtive file path, if exists
107
+ * @throws {Error} if unable to find relative path
108
+ */
109
+ _parseFilePath(localFilePath) {
110
+ let parsedFilePath;
111
+ try {
112
+ parsedFilePath = path.join(import_meta.dirname, localFilePath);
113
+ } catch (error) {
114
+ throw new Error(`Error parsing file path: ${error}`);
115
+ }
116
+ return parsedFilePath ? parsedFilePath : void 0;
117
+ }
118
+ /**
119
+ * Generates FormData from a file, according to census geocoder specs
120
+ * @param {string} filePath
121
+ * @returns {FormData} Formatted form data, converting the file into a {Blob}
122
+ */
123
+ _createFormData(filePath) {
124
+ const fileBuffer = fs.readFileSync(filePath);
125
+ if (!fileBuffer) throw new Error(`Unable to read file ${filePath}`);
126
+ const blob = new Blob([fileBuffer], { type: "text/plain" });
127
+ const formData = new FormData();
128
+ formData.append("addressFile", blob, path.basename(filePath));
129
+ formData.append("benchmark", "Public_AR_Current");
130
+ formData.append("returntype", "location");
131
+ return formData;
132
+ }
133
+ /**
134
+ * Sends generated FormData from blob-ified {@link inputFilePath} to Census Geocoder endpoint,
135
+ * and writes response to a local file based on {@link outputFilePath}.
136
+ * @returns {void}
137
+ */
138
+ async geocodeBatch() {
139
+ const formData = this._createFormData(
140
+ this.inputFilePath
141
+ );
142
+ if (!formData)
143
+ throw new Error(
144
+ `Error creating FormData from ${this.inputFilePath}`
145
+ );
146
+ const response = await fetch(this.url, {
147
+ method: "POST",
148
+ body: formData
149
+ });
150
+ if (!response.ok) throw new Error(`HTTP Error: ${response.status}`);
151
+ const responseText = await response.text();
152
+ fs.writeFileSync(this.outputFilePath, responseText);
153
+ console.log(`Saved to ${this.outputFilePath}`);
154
+ }
155
+ };
156
+ // Annotate the CommonJS export names for ESM import in node:
157
+ 0 && (module.exports = {
158
+ BatchAddressGeocoder,
159
+ geocodeOneLineAddress,
160
+ geocodeOneLineAddressAll
161
+ });
162
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/one-line-address.ts","../src/batch-address.ts"],"sourcesContent":["/**\n * @file Library entry point for one-line and batch geocoding functions\n * @module lance-ts\n * @author Matthew Morton\n * @see {@link https://github.com/matt-the-thew/lance-ts}\n */\nexport * from \"./one-line-address.js\";\nexport * from \"./batch-address.js\";\n","/**\n * Interface for lat/lng coordinates\n * @interface CensusCoordinates\n */\nexport interface CensusCoordinates {\n x: number;\n y: number;\n}\n\n/**\n * Interface for a single coordinate pair as {@type CensusCoordinates}\n * and the associated matched address, as {@type string}\n * @interface OneLineAddressMatch\n */\nexport interface OneLineAddressMatch {\n coordinates: CensusCoordinates;\n matchedAddress: string;\n}\n\n/**\n * Interface representing an array of {@type OneLineAddressMatch}\n * @interface OneLineAddressResult\n */\nexport interface OneLineAddressResult {\n addressMatches: OneLineAddressMatch[];\n}\n\n/**\n * Interface for the optional result, of type {@type OneLineAddressResult}\n */\nexport interface OneLineAddressResponse {\n result?: OneLineAddressResult;\n}\n\n/**\n * Returns the first result from whatever address matches it receives\n * @param address {string}\n * @returns {OneLineAddressMatch}\n * @throws {Error} if response status is not 200 OK\n */\nexport async function geocodeOneLineAddress(\n address: string,\n): Promise<OneLineAddressMatch | undefined> {\n //format parameters to use latest census data\n const params = new URLSearchParams({\n address,\n benchmark: \"Public_AR_Current\",\n format: \"json\",\n });\n\n const url = `https://geocoding.geo.census.gov/geocoder/locations/onelineaddress?${params}`;\n const response: Response = await fetch(url);\n if (!response.ok) throw new Error(`HTTP error: ${response.status}`);\n\n // Retreives array of coordinates and string of matched addresses from API res\n const data = (await response.json()) as OneLineAddressResponse;\n const matches = data.result?.addressMatches;\n\n if (!matches?.length) {\n console.log(`No matches found for: ${address}`);\n return;\n }\n\n // warns if multiple matched addresses exist for a single address\n if (matches?.length > 1) {\n console.warn(`Multiple address matches found for ${address}`);\n for (const match of matches) {\n console.warn(`[WARNING}: multimple matched addresses for: ${match.matchedAddress}\n Coordinates:\n X: ${match.coordinates[\"x\"]}\n Y: ${match.coordinates[\"y\"]}\n Consider geocodeOneLineAddressAll()`);\n }\n }\n\n if (matches[0]) {\n return {\n coordinates: matches[0].coordinates,\n matchedAddress: matches[0].matchedAddress,\n };\n }\n}\n\n/**\n * Returns entire array of {@type OneLineAddressMatch}\n * @param {string} address - The address to submit to the census geocoder\n * @returns {OneLineAddressMatch[]} | undefined\n * @throws {Error} if response status is not OK\n */\nexport async function geocodeOneLineAddressAll(\n address: string,\n): Promise<OneLineAddressMatch[] | undefined> {\n //format parameters to use latest census data\n const params = new URLSearchParams({\n address,\n benchmark: \"Public_AR_Current\",\n format: \"json\",\n });\n\n const url = `https://geocoding.geo.census.gov/geocoder/locations/onelineaddress?${params}`;\n const response: Response = await fetch(url);\n if (!response.ok) throw new Error(`HTTP error: ${response.status}`);\n\n // Retreives array of coordinates and string of matched addresses from API res\n const data = (await response.json()) as OneLineAddressResponse;\n const matches = data.result?.addressMatches;\n\n if (!matches?.length) {\n console.log(`No matches found for: ${address}`);\n return;\n }\n\n return matches;\n}\n","import * as fs from \"node:fs\";\nimport * as path from \"path\";\n\n/**\n * Batch geocoder session handler\n * @param {string} inputFilePath\n * @param {string} outputFilePath - @default\n */\nexport class BatchAddressGeocoder {\n // Set geocoder endpoint\n url = \"https://geocoding.geo.census.gov/geocoder/locations/addressbatch\";\n\n inputFilePath: string;\n outputFilePath: string = \"batch_coordinates.csv\";\n\n constructor(inputFilePath: string, outputFilePath?: string) {\n this.inputFilePath = inputFilePath;\n if (outputFilePath) this.outputFilePath = outputFilePath;\n }\n\n /**\n * Finds relative path for a file name\n * @param {string} localFilePath\n * @returns {string | undefined} the realtive file path, if exists\n * @throws {Error} if unable to find relative path\n */\n _parseFilePath(localFilePath: string): string | undefined {\n // create absolute directory name for fs methods\n let parsedFilePath: string;\n try {\n parsedFilePath = path.join(import.meta.dirname, localFilePath);\n } catch (error) {\n throw new Error(`Error parsing file path: ${error}`);\n }\n\n return parsedFilePath ? parsedFilePath : undefined;\n }\n\n /**\n * Generates FormData from a file, according to census geocoder specs\n * @param {string} filePath\n * @returns {FormData} Formatted form data, converting the file into a {Blob}\n */\n _createFormData(filePath: string): FormData | undefined {\n const fileBuffer = fs.readFileSync(filePath);\n if (!fileBuffer) throw new Error(`Unable to read file ${filePath}`);\n\n const blob = new Blob([fileBuffer], { type: \"text/plain\" });\n const formData = new FormData();\n\n formData.append(\"addressFile\", blob, path.basename(filePath));\n formData.append(\"benchmark\", \"Public_AR_Current\");\n formData.append(\"returntype\", \"location\");\n\n return formData;\n }\n\n /**\n * Sends generated FormData from blob-ified {@link inputFilePath} to Census Geocoder endpoint,\n * and writes response to a local file based on {@link outputFilePath}.\n * @returns {void}\n */\n async geocodeBatch(): Promise<void> {\n const formData: FormData | undefined = this._createFormData(\n this.inputFilePath,\n );\n if (!formData)\n throw new Error(\n `Error creating FormData from ${this.inputFilePath}`,\n );\n const response = await fetch(this.url, {\n method: \"POST\",\n body: formData,\n });\n if (!response.ok) throw new Error(`HTTP Error: ${response.status}`);\n const responseText = await response.text();\n\n fs.writeFileSync(this.outputFilePath, responseText);\n console.log(`Saved to ${this.outputFilePath}`);\n }\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,77 @@
1
+ /**
2
+ * Interface for lat/lng coordinates
3
+ * @interface CensusCoordinates
4
+ */
5
+ interface CensusCoordinates {
6
+ x: number;
7
+ y: number;
8
+ }
9
+ /**
10
+ * Interface for a single coordinate pair as {@type CensusCoordinates}
11
+ * and the associated matched address, as {@type string}
12
+ * @interface OneLineAddressMatch
13
+ */
14
+ interface OneLineAddressMatch {
15
+ coordinates: CensusCoordinates;
16
+ matchedAddress: string;
17
+ }
18
+ /**
19
+ * Interface representing an array of {@type OneLineAddressMatch}
20
+ * @interface OneLineAddressResult
21
+ */
22
+ interface OneLineAddressResult {
23
+ addressMatches: OneLineAddressMatch[];
24
+ }
25
+ /**
26
+ * Interface for the optional result, of type {@type OneLineAddressResult}
27
+ */
28
+ interface OneLineAddressResponse {
29
+ result?: OneLineAddressResult;
30
+ }
31
+ /**
32
+ * Returns the first result from whatever address matches it receives
33
+ * @param address {string}
34
+ * @returns {OneLineAddressMatch}
35
+ * @throws {Error} if response status is not 200 OK
36
+ */
37
+ declare function geocodeOneLineAddress(address: string): Promise<OneLineAddressMatch | undefined>;
38
+ /**
39
+ * Returns entire array of {@type OneLineAddressMatch}
40
+ * @param {string} address - The address to submit to the census geocoder
41
+ * @returns {OneLineAddressMatch[]} | undefined
42
+ * @throws {Error} if response status is not OK
43
+ */
44
+ declare function geocodeOneLineAddressAll(address: string): Promise<OneLineAddressMatch[] | undefined>;
45
+
46
+ /**
47
+ * Batch geocoder session handler
48
+ * @param {string} inputFilePath
49
+ * @param {string} outputFilePath - @default
50
+ */
51
+ declare class BatchAddressGeocoder {
52
+ url: string;
53
+ inputFilePath: string;
54
+ outputFilePath: string;
55
+ constructor(inputFilePath: string, outputFilePath?: string);
56
+ /**
57
+ * Finds relative path for a file name
58
+ * @param {string} localFilePath
59
+ * @returns {string | undefined} the realtive file path, if exists
60
+ * @throws {Error} if unable to find relative path
61
+ */
62
+ _parseFilePath(localFilePath: string): string | undefined;
63
+ /**
64
+ * Generates FormData from a file, according to census geocoder specs
65
+ * @param {string} filePath
66
+ * @returns {FormData} Formatted form data, converting the file into a {Blob}
67
+ */
68
+ _createFormData(filePath: string): FormData | undefined;
69
+ /**
70
+ * Sends generated FormData from blob-ified {@link inputFilePath} to Census Geocoder endpoint,
71
+ * and writes response to a local file based on {@link outputFilePath}.
72
+ * @returns {void}
73
+ */
74
+ geocodeBatch(): Promise<void>;
75
+ }
76
+
77
+ export { BatchAddressGeocoder, type CensusCoordinates, type OneLineAddressMatch, type OneLineAddressResponse, type OneLineAddressResult, geocodeOneLineAddress, geocodeOneLineAddressAll };
package/dist/index.d.ts CHANGED
@@ -1,9 +1,77 @@
1
1
  /**
2
- * @file Library entry point for one-line and batch geocoding functions
3
- * @module lance-ts
4
- * @author Matthew Morton
5
- * @see {@link https://github.com/matt-the-thew/lance-ts}
6
- */
7
- export * from "./one-line-address.js";
8
- export * from "./batch-address.js";
9
- //# sourceMappingURL=index.d.ts.map
2
+ * Interface for lat/lng coordinates
3
+ * @interface CensusCoordinates
4
+ */
5
+ interface CensusCoordinates {
6
+ x: number;
7
+ y: number;
8
+ }
9
+ /**
10
+ * Interface for a single coordinate pair as {@type CensusCoordinates}
11
+ * and the associated matched address, as {@type string}
12
+ * @interface OneLineAddressMatch
13
+ */
14
+ interface OneLineAddressMatch {
15
+ coordinates: CensusCoordinates;
16
+ matchedAddress: string;
17
+ }
18
+ /**
19
+ * Interface representing an array of {@type OneLineAddressMatch}
20
+ * @interface OneLineAddressResult
21
+ */
22
+ interface OneLineAddressResult {
23
+ addressMatches: OneLineAddressMatch[];
24
+ }
25
+ /**
26
+ * Interface for the optional result, of type {@type OneLineAddressResult}
27
+ */
28
+ interface OneLineAddressResponse {
29
+ result?: OneLineAddressResult;
30
+ }
31
+ /**
32
+ * Returns the first result from whatever address matches it receives
33
+ * @param address {string}
34
+ * @returns {OneLineAddressMatch}
35
+ * @throws {Error} if response status is not 200 OK
36
+ */
37
+ declare function geocodeOneLineAddress(address: string): Promise<OneLineAddressMatch | undefined>;
38
+ /**
39
+ * Returns entire array of {@type OneLineAddressMatch}
40
+ * @param {string} address - The address to submit to the census geocoder
41
+ * @returns {OneLineAddressMatch[]} | undefined
42
+ * @throws {Error} if response status is not OK
43
+ */
44
+ declare function geocodeOneLineAddressAll(address: string): Promise<OneLineAddressMatch[] | undefined>;
45
+
46
+ /**
47
+ * Batch geocoder session handler
48
+ * @param {string} inputFilePath
49
+ * @param {string} outputFilePath - @default
50
+ */
51
+ declare class BatchAddressGeocoder {
52
+ url: string;
53
+ inputFilePath: string;
54
+ outputFilePath: string;
55
+ constructor(inputFilePath: string, outputFilePath?: string);
56
+ /**
57
+ * Finds relative path for a file name
58
+ * @param {string} localFilePath
59
+ * @returns {string | undefined} the realtive file path, if exists
60
+ * @throws {Error} if unable to find relative path
61
+ */
62
+ _parseFilePath(localFilePath: string): string | undefined;
63
+ /**
64
+ * Generates FormData from a file, according to census geocoder specs
65
+ * @param {string} filePath
66
+ * @returns {FormData} Formatted form data, converting the file into a {Blob}
67
+ */
68
+ _createFormData(filePath: string): FormData | undefined;
69
+ /**
70
+ * Sends generated FormData from blob-ified {@link inputFilePath} to Census Geocoder endpoint,
71
+ * and writes response to a local file based on {@link outputFilePath}.
72
+ * @returns {void}
73
+ */
74
+ geocodeBatch(): Promise<void>;
75
+ }
76
+
77
+ export { BatchAddressGeocoder, type CensusCoordinates, type OneLineAddressMatch, type OneLineAddressResponse, type OneLineAddressResult, geocodeOneLineAddress, geocodeOneLineAddressAll };
package/dist/index.js CHANGED
@@ -1,9 +1,122 @@
1
- /**
2
- * @file Library entry point for one-line and batch geocoding functions
3
- * @module lance-ts
4
- * @author Matthew Morton
5
- * @see {@link https://github.com/matt-the-thew/lance-ts}
6
- */
7
- export * from "./one-line-address.js";
8
- export * from "./batch-address.js";
1
+ // src/one-line-address.ts
2
+ async function geocodeOneLineAddress(address) {
3
+ const params = new URLSearchParams({
4
+ address,
5
+ benchmark: "Public_AR_Current",
6
+ format: "json"
7
+ });
8
+ const url = `https://geocoding.geo.census.gov/geocoder/locations/onelineaddress?${params}`;
9
+ const response = await fetch(url);
10
+ if (!response.ok) throw new Error(`HTTP error: ${response.status}`);
11
+ const data = await response.json();
12
+ const matches = data.result?.addressMatches;
13
+ if (!matches?.length) {
14
+ console.log(`No matches found for: ${address}`);
15
+ return;
16
+ }
17
+ if (matches?.length > 1) {
18
+ console.warn(`Multiple address matches found for ${address}`);
19
+ for (const match of matches) {
20
+ console.warn(`[WARNING}: multimple matched addresses for: ${match.matchedAddress}
21
+ Coordinates:
22
+ X: ${match.coordinates["x"]}
23
+ Y: ${match.coordinates["y"]}
24
+ Consider geocodeOneLineAddressAll()`);
25
+ }
26
+ }
27
+ if (matches[0]) {
28
+ return {
29
+ coordinates: matches[0].coordinates,
30
+ matchedAddress: matches[0].matchedAddress
31
+ };
32
+ }
33
+ }
34
+ async function geocodeOneLineAddressAll(address) {
35
+ const params = new URLSearchParams({
36
+ address,
37
+ benchmark: "Public_AR_Current",
38
+ format: "json"
39
+ });
40
+ const url = `https://geocoding.geo.census.gov/geocoder/locations/onelineaddress?${params}`;
41
+ const response = await fetch(url);
42
+ if (!response.ok) throw new Error(`HTTP error: ${response.status}`);
43
+ const data = await response.json();
44
+ const matches = data.result?.addressMatches;
45
+ if (!matches?.length) {
46
+ console.log(`No matches found for: ${address}`);
47
+ return;
48
+ }
49
+ return matches;
50
+ }
51
+
52
+ // src/batch-address.ts
53
+ import * as fs from "fs";
54
+ import * as path from "path";
55
+ var BatchAddressGeocoder = class {
56
+ // Set geocoder endpoint
57
+ url = "https://geocoding.geo.census.gov/geocoder/locations/addressbatch";
58
+ inputFilePath;
59
+ outputFilePath = "batch_coordinates.csv";
60
+ constructor(inputFilePath, outputFilePath) {
61
+ this.inputFilePath = inputFilePath;
62
+ if (outputFilePath) this.outputFilePath = outputFilePath;
63
+ }
64
+ /**
65
+ * Finds relative path for a file name
66
+ * @param {string} localFilePath
67
+ * @returns {string | undefined} the realtive file path, if exists
68
+ * @throws {Error} if unable to find relative path
69
+ */
70
+ _parseFilePath(localFilePath) {
71
+ let parsedFilePath;
72
+ try {
73
+ parsedFilePath = path.join(import.meta.dirname, localFilePath);
74
+ } catch (error) {
75
+ throw new Error(`Error parsing file path: ${error}`);
76
+ }
77
+ return parsedFilePath ? parsedFilePath : void 0;
78
+ }
79
+ /**
80
+ * Generates FormData from a file, according to census geocoder specs
81
+ * @param {string} filePath
82
+ * @returns {FormData} Formatted form data, converting the file into a {Blob}
83
+ */
84
+ _createFormData(filePath) {
85
+ const fileBuffer = fs.readFileSync(filePath);
86
+ if (!fileBuffer) throw new Error(`Unable to read file ${filePath}`);
87
+ const blob = new Blob([fileBuffer], { type: "text/plain" });
88
+ const formData = new FormData();
89
+ formData.append("addressFile", blob, path.basename(filePath));
90
+ formData.append("benchmark", "Public_AR_Current");
91
+ formData.append("returntype", "location");
92
+ return formData;
93
+ }
94
+ /**
95
+ * Sends generated FormData from blob-ified {@link inputFilePath} to Census Geocoder endpoint,
96
+ * and writes response to a local file based on {@link outputFilePath}.
97
+ * @returns {void}
98
+ */
99
+ async geocodeBatch() {
100
+ const formData = this._createFormData(
101
+ this.inputFilePath
102
+ );
103
+ if (!formData)
104
+ throw new Error(
105
+ `Error creating FormData from ${this.inputFilePath}`
106
+ );
107
+ const response = await fetch(this.url, {
108
+ method: "POST",
109
+ body: formData
110
+ });
111
+ if (!response.ok) throw new Error(`HTTP Error: ${response.status}`);
112
+ const responseText = await response.text();
113
+ fs.writeFileSync(this.outputFilePath, responseText);
114
+ console.log(`Saved to ${this.outputFilePath}`);
115
+ }
116
+ };
117
+ export {
118
+ BatchAddressGeocoder,
119
+ geocodeOneLineAddress,
120
+ geocodeOneLineAddressAll
121
+ };
9
122
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,cAAc,uBAAuB,CAAC;AACtC,cAAc,oBAAoB,CAAC"}
1
+ {"version":3,"sources":["../src/one-line-address.ts","../src/batch-address.ts"],"sourcesContent":["/**\n * Interface for lat/lng coordinates\n * @interface CensusCoordinates\n */\nexport interface CensusCoordinates {\n x: number;\n y: number;\n}\n\n/**\n * Interface for a single coordinate pair as {@type CensusCoordinates}\n * and the associated matched address, as {@type string}\n * @interface OneLineAddressMatch\n */\nexport interface OneLineAddressMatch {\n coordinates: CensusCoordinates;\n matchedAddress: string;\n}\n\n/**\n * Interface representing an array of {@type OneLineAddressMatch}\n * @interface OneLineAddressResult\n */\nexport interface OneLineAddressResult {\n addressMatches: OneLineAddressMatch[];\n}\n\n/**\n * Interface for the optional result, of type {@type OneLineAddressResult}\n */\nexport interface OneLineAddressResponse {\n result?: OneLineAddressResult;\n}\n\n/**\n * Returns the first result from whatever address matches it receives\n * @param address {string}\n * @returns {OneLineAddressMatch}\n * @throws {Error} if response status is not 200 OK\n */\nexport async function geocodeOneLineAddress(\n address: string,\n): Promise<OneLineAddressMatch | undefined> {\n //format parameters to use latest census data\n const params = new URLSearchParams({\n address,\n benchmark: \"Public_AR_Current\",\n format: \"json\",\n });\n\n const url = `https://geocoding.geo.census.gov/geocoder/locations/onelineaddress?${params}`;\n const response: Response = await fetch(url);\n if (!response.ok) throw new Error(`HTTP error: ${response.status}`);\n\n // Retreives array of coordinates and string of matched addresses from API res\n const data = (await response.json()) as OneLineAddressResponse;\n const matches = data.result?.addressMatches;\n\n if (!matches?.length) {\n console.log(`No matches found for: ${address}`);\n return;\n }\n\n // warns if multiple matched addresses exist for a single address\n if (matches?.length > 1) {\n console.warn(`Multiple address matches found for ${address}`);\n for (const match of matches) {\n console.warn(`[WARNING}: multimple matched addresses for: ${match.matchedAddress}\n Coordinates:\n X: ${match.coordinates[\"x\"]}\n Y: ${match.coordinates[\"y\"]}\n Consider geocodeOneLineAddressAll()`);\n }\n }\n\n if (matches[0]) {\n return {\n coordinates: matches[0].coordinates,\n matchedAddress: matches[0].matchedAddress,\n };\n }\n}\n\n/**\n * Returns entire array of {@type OneLineAddressMatch}\n * @param {string} address - The address to submit to the census geocoder\n * @returns {OneLineAddressMatch[]} | undefined\n * @throws {Error} if response status is not OK\n */\nexport async function geocodeOneLineAddressAll(\n address: string,\n): Promise<OneLineAddressMatch[] | undefined> {\n //format parameters to use latest census data\n const params = new URLSearchParams({\n address,\n benchmark: \"Public_AR_Current\",\n format: \"json\",\n });\n\n const url = `https://geocoding.geo.census.gov/geocoder/locations/onelineaddress?${params}`;\n const response: Response = await fetch(url);\n if (!response.ok) throw new Error(`HTTP error: ${response.status}`);\n\n // Retreives array of coordinates and string of matched addresses from API res\n const data = (await response.json()) as OneLineAddressResponse;\n const matches = data.result?.addressMatches;\n\n if (!matches?.length) {\n console.log(`No matches found for: ${address}`);\n return;\n }\n\n return matches;\n}\n","import * as fs from \"node:fs\";\nimport * as path from \"path\";\n\n/**\n * Batch geocoder session handler\n * @param {string} inputFilePath\n * @param {string} outputFilePath - @default\n */\nexport class BatchAddressGeocoder {\n // Set geocoder endpoint\n url = \"https://geocoding.geo.census.gov/geocoder/locations/addressbatch\";\n\n inputFilePath: string;\n outputFilePath: string = \"batch_coordinates.csv\";\n\n constructor(inputFilePath: string, outputFilePath?: string) {\n this.inputFilePath = inputFilePath;\n if (outputFilePath) this.outputFilePath = outputFilePath;\n }\n\n /**\n * Finds relative path for a file name\n * @param {string} localFilePath\n * @returns {string | undefined} the realtive file path, if exists\n * @throws {Error} if unable to find relative path\n */\n _parseFilePath(localFilePath: string): string | undefined {\n // create absolute directory name for fs methods\n let parsedFilePath: string;\n try {\n parsedFilePath = path.join(import.meta.dirname, localFilePath);\n } catch (error) {\n throw new Error(`Error parsing file path: ${error}`);\n }\n\n return parsedFilePath ? parsedFilePath : undefined;\n }\n\n /**\n * Generates FormData from a file, according to census geocoder specs\n * @param {string} filePath\n * @returns {FormData} Formatted form data, converting the file into a {Blob}\n */\n _createFormData(filePath: string): FormData | undefined {\n const fileBuffer = fs.readFileSync(filePath);\n if (!fileBuffer) throw new Error(`Unable to read file ${filePath}`);\n\n const blob = new Blob([fileBuffer], { type: \"text/plain\" });\n const formData = new FormData();\n\n formData.append(\"addressFile\", blob, path.basename(filePath));\n formData.append(\"benchmark\", \"Public_AR_Current\");\n formData.append(\"returntype\", \"location\");\n\n return formData;\n }\n\n /**\n * Sends generated FormData from blob-ified {@link inputFilePath} to Census Geocoder endpoint,\n * and writes response to a local file based on {@link outputFilePath}.\n * @returns {void}\n */\n async geocodeBatch(): Promise<void> {\n const formData: FormData | undefined = this._createFormData(\n this.inputFilePath,\n );\n if (!formData)\n throw new Error(\n `Error creating FormData from ${this.inputFilePath}`,\n );\n const response = await fetch(this.url, {\n method: \"POST\",\n body: formData,\n });\n if (!response.ok) throw new Error(`HTTP Error: ${response.status}`);\n const responseText = await response.text();\n\n fs.writeFileSync(this.outputFilePath, responseText);\n console.log(`Saved to ${this.outputFilePath}`);\n }\n}\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,37 +1,44 @@
1
1
  {
2
2
  "name": "lance-ts",
3
- "version": "1.0.0",
4
- "description": "Lat/Lng from Address via National Census Endpoint",
3
+ "version": "1.0.1",
4
+ "description": "Lat/Long from National Census Endpoint. An opinionated, blazingingly fast geocoding library.",
5
5
  "type": "module",
6
- "main": "index.js",
6
+ "module": "./dist/index.mjs",
7
+ "types": "./dist/index.d.ts",
7
8
  "exports": {
8
9
  ".": {
9
- "import": "./dist/esm/index.js",
10
- "types": "./dist/index.js",
11
- "require": "./dist/cjs/index.js"
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.js",
12
+ "require": "./dist/index.cjs"
12
13
  }
13
14
  },
14
- "files": [
15
- "dist"
16
- ],
17
15
  "scripts": {
18
16
  "test": "vitest",
19
- "build": "tsc",
20
- "prepublishOnly": "pnpm run build"
17
+ "test:ui": "vitest --ui",
18
+ "type-check": "tsc --noEmit",
19
+ "build": "tsup",
20
+ "clean": "rm -rf ./dist"
21
21
  },
22
- "keywords": [],
23
- "author": "",
22
+ "files": [
23
+ "dist"
24
+ ],
25
+ "keywords": [
26
+ "geocoding",
27
+ "gps",
28
+ "geography",
29
+ "geospatial",
30
+ "coordinates"
31
+ ],
32
+ "author": "Matthew Morton <mmortondev@gmail.com>",
24
33
  "license": "ISC",
25
- "packageManager": "pnpm@10.30.3",
34
+ "packageManager": "pnpm@11.5.2",
26
35
  "devDependencies": {
27
- "@types/node": "^25.9.1",
28
- "@vitest/ui": "4.1.7",
36
+ "@types/node": "^25.9.2",
37
+ "@vitest/ui": "4.1.8",
38
+ "memfs": "^4.57.6",
29
39
  "publint": "^0.3.21",
30
40
  "tsup": "^8.5.1",
31
- "typescript": "^6.0.3"
32
- },
33
- "dependencies": {
34
- "memfs": "^4.57.2",
35
- "vitest": "^4.1.7"
41
+ "typescript": "^6.0.3",
42
+ "vitest": "^4.1.8"
36
43
  }
37
44
  }
@@ -1,31 +0,0 @@
1
- /**
2
- * Batch geocoder session handler
3
- * @param {string} inputFilePath
4
- * @param {string} outputFilePath - @default
5
- */
6
- export declare class BatchAddressGeocoder {
7
- url: string;
8
- inputFilePath: string;
9
- outputFilePath: string;
10
- constructor(inputFilePath: string, outputFilePath?: string);
11
- /**
12
- * Finds relative path for a file name
13
- * @param {string} localFilePath
14
- * @returns {string | undefined} the realtive file path, if exists
15
- * @throws {Error} if unable to find relative path
16
- */
17
- _parseFilePath(localFilePath: string): string | undefined;
18
- /**
19
- * Generates FormData from a file, according to census geocoder specs
20
- * @param {string} filePath
21
- * @returns {FormData} Formatted form data, converting the file into a {Blob}
22
- */
23
- _createFormData(filePath: string): FormData | undefined;
24
- /**
25
- * Sends generated FormData from blob-ified {@link inputFilePath} to Census Geocoder endpoint,
26
- * and writes response to a local file based on {@link outputFilePath}.
27
- * @returns {void}
28
- */
29
- geocodeBatch(): Promise<void>;
30
- }
31
- //# sourceMappingURL=batch-address.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"batch-address.d.ts","sourceRoot":"","sources":["../src/batch-address.ts"],"names":[],"mappings":"AAKA;;;;GAIG;AACH,qBAAa,oBAAoB;IAE/B,GAAG,SAAsE;IAEzE,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAA2B;gBAErC,aAAa,EAAE,MAAM,EAAE,cAAc,CAAC,EAAE,MAAM;IAK1D;;;;;OAKG;IACH,cAAc,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAYzD;;;;OAIG;IACH,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS;IAcvD;;;;OAIG;IACG,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC;CAkBpC"}
@@ -1,70 +0,0 @@
1
- import * as fs from "node:fs";
2
- import * as path from "path";
3
- /**
4
- * Batch geocoder session handler
5
- * @param {string} inputFilePath
6
- * @param {string} outputFilePath - @default
7
- */
8
- export class BatchAddressGeocoder {
9
- constructor(inputFilePath, outputFilePath) {
10
- // Set geocoder endpoint
11
- this.url = "https://geocoding.geo.census.gov/geocoder/locations/addressbatch";
12
- this.outputFilePath = "batch_coordinates.csv";
13
- this.inputFilePath = inputFilePath;
14
- if (outputFilePath)
15
- this.outputFilePath = outputFilePath;
16
- }
17
- /**
18
- * Finds relative path for a file name
19
- * @param {string} localFilePath
20
- * @returns {string | undefined} the realtive file path, if exists
21
- * @throws {Error} if unable to find relative path
22
- */
23
- _parseFilePath(localFilePath) {
24
- // create absolute directory name for fs methods
25
- let parsedFilePath;
26
- try {
27
- parsedFilePath = path.join(import.meta.dirname, localFilePath);
28
- }
29
- catch (error) {
30
- throw new Error(`Error parsing file path: ${error}`);
31
- }
32
- return parsedFilePath ? parsedFilePath : undefined;
33
- }
34
- /**
35
- * Generates FormData from a file, according to census geocoder specs
36
- * @param {string} filePath
37
- * @returns {FormData} Formatted form data, converting the file into a {Blob}
38
- */
39
- _createFormData(filePath) {
40
- const fileBuffer = fs.readFileSync(filePath);
41
- if (!fileBuffer)
42
- throw new Error(`Unable to read file ${filePath}`);
43
- const blob = new Blob([fileBuffer], { type: "text/plain" });
44
- const formData = new FormData();
45
- formData.append("addressFile", blob, path.basename(filePath));
46
- formData.append("benchmark", "Public_AR_Current");
47
- formData.append("returntype", "location");
48
- return formData;
49
- }
50
- /**
51
- * Sends generated FormData from blob-ified {@link inputFilePath} to Census Geocoder endpoint,
52
- * and writes response to a local file based on {@link outputFilePath}.
53
- * @returns {void}
54
- */
55
- async geocodeBatch() {
56
- const formData = this._createFormData(this.inputFilePath);
57
- if (!formData)
58
- throw new Error(`Error creating FormData from ${this.inputFilePath}`);
59
- const response = await fetch(this.url, {
60
- method: "POST",
61
- body: formData,
62
- });
63
- if (!response.ok)
64
- throw new Error(`HTTP Error: ${response.status}`);
65
- const responseText = await response.text();
66
- fs.writeFileSync(this.outputFilePath, responseText);
67
- console.log(`Saved to ${this.outputFilePath}`);
68
- }
69
- }
70
- //# sourceMappingURL=batch-address.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"batch-address.js","sourceRoot":"","sources":["../src/batch-address.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAI7B;;;;GAIG;AACH,MAAM,OAAO,oBAAoB;IAO/B,YAAY,aAAqB,EAAE,cAAuB;QAN1D,wBAAwB;QACxB,QAAG,GAAG,kEAAkE,CAAC;QAGzE,mBAAc,GAAW,uBAAuB,CAAC;QAG/C,IAAI,CAAC,aAAa,GAAG,aAAa,CAAC;QACnC,IAAI,cAAc;YAAE,IAAI,CAAC,cAAc,GAAG,cAAc,CAAC;IAC3D,CAAC;IAED;;;;;OAKG;IACH,cAAc,CAAC,aAAqB;QAClC,gDAAgD;QAChD,IAAI,cAAsB,CAAC;QAC3B,IAAI,CAAC;YACH,cAAc,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,aAAa,CAAC,CAAC;QACjE,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,IAAI,KAAK,CAAC,4BAA4B,KAAK,EAAE,CAAC,CAAC;QACvD,CAAC;QAED,OAAO,cAAc,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,SAAS,CAAC;IACrD,CAAC;IAED;;;;OAIG;IACH,eAAe,CAAC,QAAgB;QAC9B,MAAM,UAAU,GAAG,EAAE,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC;QAC7C,IAAI,CAAC,UAAU;YAAE,MAAM,IAAI,KAAK,CAAC,uBAAuB,QAAQ,EAAE,CAAC,CAAC;QAEpE,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,CAAC,UAAU,CAAC,EAAE,EAAE,IAAI,EAAE,YAAY,EAAE,CAAC,CAAC;QAC5D,MAAM,QAAQ,GAAG,IAAI,QAAQ,EAAE,CAAC;QAEhC,QAAQ,CAAC,MAAM,CAAC,aAAa,EAAE,IAAI,EAAE,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC;QAC9D,QAAQ,CAAC,MAAM,CAAC,WAAW,EAAE,mBAAmB,CAAC,CAAC;QAClD,QAAQ,CAAC,MAAM,CAAC,YAAY,EAAE,UAAU,CAAC,CAAC;QAE1C,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,YAAY;QAChB,MAAM,QAAQ,GAAyB,IAAI,CAAC,eAAe,CACzD,IAAI,CAAC,aAAa,CACnB,CAAC;QACF,IAAI,CAAC,QAAQ;YACX,MAAM,IAAI,KAAK,CACb,gCAAgC,IAAI,CAAC,aAAa,EAAE,CACrD,CAAC;QACJ,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE;YACrC,MAAM,EAAE,MAAM;YACd,IAAI,EAAE,QAAQ;SACf,CAAC,CAAC;QACH,IAAI,CAAC,QAAQ,CAAC,EAAE;YAAE,MAAM,IAAI,KAAK,CAAC,eAAe,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;QACpE,MAAM,YAAY,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;QAE3C,EAAE,CAAC,aAAa,CAAC,IAAI,CAAC,cAAc,EAAE,YAAY,CAAC,CAAC;QACpD,OAAO,CAAC,GAAG,CAAC,YAAY,IAAI,CAAC,cAAc,EAAE,CAAC,CAAC;IACjD,CAAC;CACF"}
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=batch-address.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"batch-address.test.d.ts","sourceRoot":"","sources":["../src/batch-address.test.ts"],"names":[],"mappings":""}
@@ -1,98 +0,0 @@
1
- import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
2
- import { fs, vol } from "memfs";
3
- import * as path from "path";
4
- vi.mock("node:fs", async () => {
5
- const { fs } = await import("memfs");
6
- return fs;
7
- });
8
- //import BatchAddressGeocoder AFTER fs is mocked
9
- //so it initializes pointed at mocked module
10
- import { BatchAddressGeocoder } from "./batch-address.js";
11
- describe("BatchAddressGeocoder", () => {
12
- let geocoder;
13
- // set up memfs dir and file
14
- const mockFilePath = "/out/mockfile.csv";
15
- // mock function to replace fetch
16
- const fetchMock = vi.fn();
17
- // set test class function
18
- beforeEach(() => {
19
- // make fetch mock func globally available
20
- vi.stubGlobal("fetch", fetchMock);
21
- // reset the state of in-memory fs
22
- vol.reset();
23
- //generate entire filetree
24
- vol.mkdirSync("/out", { recursive: true });
25
- //initialize geocoder
26
- geocoder = new BatchAddressGeocoder("");
27
- });
28
- afterEach(() => {
29
- vi.unstubAllGlobals();
30
- vi.resetAllMocks();
31
- });
32
- it("initializes with the correct endpoint", () => {
33
- expect(geocoder.url).toEqual("https://geocoding.geo.census.gov/geocoder/locations/addressbatch");
34
- });
35
- describe("_parseFilePath", () => {
36
- it("correctly parses the absolute file path", () => {
37
- //generate mockFile
38
- vol.writeFileSync(mockFilePath, "this is some data");
39
- //point geocoder at mock file
40
- geocoder.inputFilePath = mockFilePath;
41
- // get absolute path from BatchAddressGeocoder
42
- const parsedPath = geocoder._parseFilePath(geocoder.inputFilePath);
43
- // it should exist
44
- expect(parsedPath);
45
- if (parsedPath) {
46
- // it should be absolute
47
- expect(path.isAbsolute(parsedPath));
48
- }
49
- });
50
- });
51
- describe("_createFormData", async () => {
52
- it("generates accurate form data", async () => {
53
- //initialize and write to mock file
54
- const mockFilePath = "/mockfile.csv";
55
- vol.writeFileSync(mockFilePath, "this is some data");
56
- //point geocoder at mocked file
57
- geocoder.inputFilePath = mockFilePath;
58
- // generate form data
59
- const formData = geocoder._createFormData(geocoder.inputFilePath);
60
- // the file field should exist
61
- expect(formData.get("addressFile")).toBeDefined();
62
- // it should generate a blob
63
- const fileField = formData.get("addressFile");
64
- expect(fileField).toBeInstanceOf(Blob);
65
- // it should hold data
66
- expect(fileField.size).toBeGreaterThan(0);
67
- // it should be text/plain
68
- expect(fileField.type).toBe("text/plain");
69
- // content should be accurate
70
- const text = await fileField.text();
71
- expect(text).toBe("this is some data");
72
- });
73
- });
74
- describe("geocodeBatch", async () => {
75
- it("returns correct data in .csv format", async () => {
76
- const fetchSpy = vi.spyOn(global, "fetch");
77
- //initialize and write to mock file
78
- vol.writeFileSync(mockFilePath, "FIELD 1, FIELD 2, FIELD 3, FIELD 4");
79
- const geocoder = new BatchAddressGeocoder(mockFilePath, "/out/output.csv");
80
- const geocodeBatchSpy = vi.spyOn(geocoder, "geocodeBatch");
81
- //mock file blob response
82
- fetchMock.mockResolvedValue({
83
- ok: true,
84
- status: 200,
85
- text: () => Promise.resolve("FIELD 1, FIELD 2, FIELD 3, FIELD 4"),
86
- });
87
- await geocoder.geocodeBatch();
88
- //geocode function should be called once
89
- expect(geocodeBatchSpy).toHaveBeenCalledOnce();
90
- //output file should exist
91
- expect(fs.existsSync("/out/output.csv")).toBeTruthy;
92
- //fetch should have been called once
93
- expect(fetchSpy).toHaveBeenCalledOnce();
94
- expect(fs.readFileSync("/out/output.csv", "utf-8")).toContain("FIELD 1, FIELD 2, FIELD 3, FIELD 4");
95
- });
96
- });
97
- });
98
- //# sourceMappingURL=batch-address.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"batch-address.test.js","sourceRoot":"","sources":["../src/batch-address.test.ts"],"names":[],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,cAAc,uBAAuB,CAAC;AACtC,cAAc,oBAAoB,CAAC"}
@@ -1,45 +0,0 @@
1
- /**
2
- * Interface for lat/lng coordinates
3
- * @interface CensusCoordinates
4
- */
5
- export interface CensusCoordinates {
6
- x: number;
7
- y: number;
8
- }
9
- /**
10
- * Interface for a single coordinate pair as {@type CensusCoordinates}
11
- * and the associated matched address, as {@type string}
12
- * @interface OneLineAddressMatch
13
- */
14
- export interface OneLineAddressMatch {
15
- coordinates: CensusCoordinates;
16
- matchedAddress: string;
17
- }
18
- /**
19
- * Interface representing an array of {@type OneLineAddressMatch}
20
- * @interface OneLineAddressResult
21
- */
22
- export interface OneLineAddressResult {
23
- addressMatches: OneLineAddressMatch[];
24
- }
25
- /**
26
- * Interface for the optional result, of type {@type OneLineAddressResult}
27
- */
28
- export interface OneLineAddressResponse {
29
- result?: OneLineAddressResult;
30
- }
31
- /**
32
- * Returns the first result from whatever address matches it receives
33
- * @param address {string}
34
- * @returns {OneLineAddressMatch}
35
- * @throws {Error} if response status is not OK
36
- */
37
- export declare function geocodeOneLineAddress(address: string): Promise<OneLineAddressMatch | undefined>;
38
- /**
39
- * Returns entire array of {@type OneLineAddressMatch}
40
- * @param {string} address - The address to submit to the census geocoder
41
- * @returns {OneLineAddressMatch[]} | undefined
42
- * @throws {Error} if response status is not OK
43
- */
44
- export declare function geocodeOneLineAddressAll(address: string): Promise<OneLineAddressMatch[] | undefined>;
45
- //# sourceMappingURL=one-line-address.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"one-line-address.d.ts","sourceRoot":"","sources":["../src/one-line-address.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,WAAW,iBAAiB;IAChC,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;CACX;AAED;;;;GAIG;AACH,MAAM,WAAW,mBAAmB;IAClC,WAAW,EAAE,iBAAiB,CAAC;IAC/B,cAAc,EAAE,MAAM,CAAC;CACxB;AAED;;;GAGG;AACH,MAAM,WAAW,oBAAoB;IACnC,cAAc,EAAE,mBAAmB,EAAE,CAAC;CACvC;AAED;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC,MAAM,CAAC,EAAE,oBAAoB,CAAC;CAC/B;AAED;;;;;GAKG;AACH,wBAAsB,qBAAqB,CACzC,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,mBAAmB,GAAG,SAAS,CAAC,CAkC1C;AAED;;;;;GAKG;AACH,wBAAsB,wBAAwB,CAC5C,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,mBAAmB,EAAE,GAAG,SAAS,CAAC,CAsB5C"}
@@ -1,64 +0,0 @@
1
- /**
2
- * Returns the first result from whatever address matches it receives
3
- * @param address {string}
4
- * @returns {OneLineAddressMatch}
5
- * @throws {Error} if response status is not OK
6
- */
7
- export async function geocodeOneLineAddress(address) {
8
- //format parameters to use latest census data
9
- const params = new URLSearchParams({
10
- address,
11
- benchmark: "Public_AR_Current",
12
- format: "json",
13
- });
14
- const url = `https://geocoding.geo.census.gov/geocoder/locations/onelineaddress?${params}`;
15
- const response = await fetch(url);
16
- if (!response.ok)
17
- throw new Error(`HTTP error: ${response.status}`);
18
- // Retreives array of coordinates and string of matched addresses from API res
19
- const data = (await response.json());
20
- const matches = data.result?.addressMatches;
21
- if (!matches?.length) {
22
- console.log(`No matches found for: ${address}`);
23
- return;
24
- }
25
- // warns if multiple matched addresses exist for a single address
26
- if (matches?.length > 1) {
27
- console.warn(`Multiple address matches found for ${address}`);
28
- for (const match of matches) {
29
- console.warn(`[WARNING}: multimple matched addresses for: ${match.matchedAddress}
30
- Coordinates:
31
- X: ${match.coordinates["x"]}
32
- Y: ${match.coordinates["y"]}
33
- Consider geocodeOneLineAddressAll()`);
34
- }
35
- }
36
- return matches[0];
37
- }
38
- /**
39
- * Returns entire array of {@type OneLineAddressMatch}
40
- * @param {string} address - The address to submit to the census geocoder
41
- * @returns {OneLineAddressMatch[]} | undefined
42
- * @throws {Error} if response status is not OK
43
- */
44
- export async function geocodeOneLineAddressAll(address) {
45
- //format parameters to use latest census data
46
- const params = new URLSearchParams({
47
- address,
48
- benchmark: "Public_AR_Current",
49
- format: "json",
50
- });
51
- const url = `https://geocoding.geo.census.gov/geocoder/locations/onelineaddress?${params}`;
52
- const response = await fetch(url);
53
- if (!response.ok)
54
- throw new Error(`HTTP error: ${response.status}`);
55
- // Retreives array of coordinates and string of matched addresses from API res
56
- const data = (await response.json());
57
- const matches = data.result?.addressMatches;
58
- if (!matches?.length) {
59
- console.log(`No matches found for: ${address}`);
60
- return;
61
- }
62
- return matches;
63
- }
64
- //# sourceMappingURL=one-line-address.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"one-line-address.js","sourceRoot":"","sources":["../src/one-line-address.ts"],"names":[],"mappings":"AAkCA;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,qBAAqB,CACzC,OAAe;IAEf,6CAA6C;IAC7C,MAAM,MAAM,GAAG,IAAI,eAAe,CAAC;QACjC,OAAO;QACP,SAAS,EAAE,mBAAmB;QAC9B,MAAM,EAAE,MAAM;KACf,CAAC,CAAC;IAEH,MAAM,GAAG,GAAG,sEAAsE,MAAM,EAAE,CAAC;IAC3F,MAAM,QAAQ,GAAa,MAAM,KAAK,CAAC,GAAG,CAAC,CAAC;IAC5C,IAAI,CAAC,QAAQ,CAAC,EAAE;QAAE,MAAM,IAAI,KAAK,CAAC,eAAe,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;IAEpE,8EAA8E;IAC9E,MAAM,IAAI,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAA2B,CAAC;IAC/D,MAAM,OAAO,GAAG,IAAI,CAAC,MAAM,EAAE,cAAc,CAAC;IAE5C,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,CAAC;QACrB,OAAO,CAAC,GAAG,CAAC,yBAAyB,OAAO,EAAE,CAAC,CAAC;QAChD,OAAO;IACT,CAAC;IAED,iEAAiE;IACjE,IAAI,OAAO,EAAE,MAAM,GAAG,CAAC,EAAE,CAAC;QACxB,OAAO,CAAC,IAAI,CAAC,sCAAsC,OAAO,EAAE,CAAC,CAAC;QAC9D,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;YAC5B,OAAO,CAAC,IAAI,CAAC,+CAA+C,KAAK,CAAC,cAAc;;WAE3E,KAAK,CAAC,WAAW,CAAC,GAAG,CAAC;WACtB,KAAK,CAAC,WAAW,CAAC,GAAG,CAAC;0CACS,CAAC,CAAC;QACxC,CAAC;IACH,CAAC;IAED,OAAO,OAAO,CAAC,CAAC,CAAC,CAAC;AACpB,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,wBAAwB,CAC5C,OAAe;IAEf,6CAA6C;IAC7C,MAAM,MAAM,GAAG,IAAI,eAAe,CAAC;QACjC,OAAO;QACP,SAAS,EAAE,mBAAmB;QAC9B,MAAM,EAAE,MAAM;KACf,CAAC,CAAC;IAEH,MAAM,GAAG,GAAG,sEAAsE,MAAM,EAAE,CAAC;IAC3F,MAAM,QAAQ,GAAa,MAAM,KAAK,CAAC,GAAG,CAAC,CAAC;IAC5C,IAAI,CAAC,QAAQ,CAAC,EAAE;QAAE,MAAM,IAAI,KAAK,CAAC,eAAe,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;IAEpE,8EAA8E;IAC9E,MAAM,IAAI,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAA2B,CAAC;IAC/D,MAAM,OAAO,GAAG,IAAI,CAAC,MAAM,EAAE,cAAc,CAAC;IAE5C,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,CAAC;QACrB,OAAO,CAAC,GAAG,CAAC,yBAAyB,OAAO,EAAE,CAAC,CAAC;QAChD,OAAO;IACT,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC"}
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=one-line-address.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"one-line-address.test.d.ts","sourceRoot":"","sources":["../src/one-line-address.test.ts"],"names":[],"mappings":""}
@@ -1,112 +0,0 @@
1
- import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
2
- import { geocodeOneLineAddress, geocodeOneLineAddressAll, } from "./one-line-address.js";
3
- // create mocking function
4
- const fetchMock = vi.fn();
5
- beforeEach(() => {
6
- // substitute mocking function for node "fetch"
7
- vi.stubGlobal("fetch", fetchMock);
8
- });
9
- afterEach(() => {
10
- // reset everything after testing
11
- vi.unstubAllGlobals();
12
- vi.resetAllMocks();
13
- });
14
- describe("geocodeOneLineAddress", () => {
15
- it("returns lat/lng coordinates when response includes a match", async () => {
16
- fetchMock.mockResolvedValue({
17
- ok: true,
18
- json: async () => ({
19
- result: {
20
- addressMatches: [
21
- {
22
- coordinates: { x: -77.0365, y: 38.8977 },
23
- matchedAddress: "1600 Pennsylvania Ave NW, Washington, DC",
24
- },
25
- ],
26
- },
27
- }),
28
- });
29
- const result = await geocodeOneLineAddress("1600 Pennsylvania Ave NW, Washington, DC");
30
- //it should exist
31
- expect(result).toBeTruthy();
32
- //it should have correct data
33
- expect(result?.coordinates).toEqual({
34
- x: -77.0365,
35
- y: 38.8977,
36
- });
37
- expect(result?.matchedAddress).toEqual("1600 Pennsylvania Ave NW, Washington, DC");
38
- //it should call fetch once per response
39
- expect(fetchMock).toHaveBeenCalledOnce();
40
- });
41
- it("warns when multiple address matches exist", async () => {
42
- //spy on warn output stream
43
- const warnSpy = vi.spyOn(console, "warn").mockImplementation(() => { });
44
- //set up mocked fetch result with multiple entires
45
- fetchMock.mockResolvedValue({
46
- ok: true,
47
- json: async () => ({
48
- result: {
49
- addressMatches: [
50
- {
51
- coordinates: { x: -77.0365, y: 38.8977 },
52
- matchedAddress: "1600 Pennsylvania Ave NW, Washington, DC",
53
- },
54
- {
55
- coordinates: { x: -78.0, y: 40.0 },
56
- matchedAddress: "1602 Pennsylvania Ave NW, Washington, DC",
57
- },
58
- ],
59
- },
60
- }),
61
- });
62
- const result = await geocodeOneLineAddress("1600 Pennsylvania Ave NW, Washington, DC");
63
- //it should exist
64
- expect(result).toBeTruthy;
65
- //it should raise a warning
66
- expect(warnSpy).toHaveBeenCalled();
67
- //it should raise the correct warning
68
- expect(warnSpy).toHaveBeenCalledWith(expect.stringContaining("Consider geocodeOneLineAddressAll()"));
69
- });
70
- });
71
- describe("geocodeOneLineAddressAll", () => {
72
- it("returns array of all matched addresses", async () => {
73
- fetchMock.mockResolvedValue({
74
- ok: true,
75
- json: async () => ({
76
- result: {
77
- addressMatches: [
78
- {
79
- coordinates: { x: -77.0365, y: 38.8977 },
80
- matchedAddress: "1600 Pennsylvania Ave NW, Washington, DC",
81
- },
82
- {
83
- coordinates: { x: -78.0, y: 40.0 },
84
- matchedAddress: "1602 Pennsylvania Ave NW, Washington, DC",
85
- },
86
- ],
87
- },
88
- }),
89
- });
90
- const result = await geocodeOneLineAddressAll("1600 Pennsylvania Ave NW, Washington, DC");
91
- //it should exist
92
- expect(result).toBeTruthy;
93
- if (!result)
94
- return false;
95
- //it should be the correct size
96
- expect(result?.length).toBe(2);
97
- //it should have correct data
98
- expect(result[0]?.coordinates).toEqual({
99
- x: -77.0365,
100
- y: 38.8977,
101
- });
102
- expect(result[0]?.matchedAddress).toEqual("1600 Pennsylvania Ave NW, Washington, DC");
103
- expect(result[1]?.coordinates).toEqual({
104
- x: -78.0,
105
- y: 40.0,
106
- });
107
- expect(result[1]?.matchedAddress).toEqual("1602 Pennsylvania Ave NW, Washington, DC");
108
- //it should call fetch once per response
109
- expect(fetchMock).toHaveBeenCalledOnce();
110
- });
111
- });
112
- //# sourceMappingURL=one-line-address.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"one-line-address.test.js","sourceRoot":"","sources":["../src/one-line-address.test.ts"],"names":[],"mappings":"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"}