catastrogps 1.2.1 → 2.1.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.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # catastrogps
2
2
 
3
- Official JavaScript and TypeScript client for the [Catastro GPS API](https://www.catastrogps.es/developers): cadastral parcels from **29 European countries (31 official cadastres, including the Basque Country and Navarre)**, with one API key.
3
+ Official JavaScript and TypeScript client for the [Catastro GPS API](https://www.catastrogps.es/developers): cadastral parcels from **29 European countries (including the Basque Country and Navarre foral cadastres)**, with one API key: by cadastral reference in 27, by coordinates in all 29.
4
4
 
5
5
  - Look up a parcel by its **official cadastral reference** or by **coordinates**, with the country detected for you.
6
6
  - Turn a **postal address into parcels** in Spain and 26 more countries, ranked by confidence.
@@ -27,7 +27,7 @@ import { CatastroGPS } from 'catastrogps';
27
27
  const client = new CatastroGPS({ apiKey: process.env.CATASTROGPS_API_KEY });
28
28
 
29
29
  const parcel = await client.parcels.get('9872023VH5797S0001WX');
30
- console.log(parcel.municipio, parcel.superficieParcela, parcel.latitud, parcel.longitud);
30
+ console.log(parcel.municipality, parcel.parcelArea, parcel.latitude, parcel.longitude);
31
31
  ```
32
32
 
33
33
  `new CatastroGPS()` with no arguments reads `CATASTROGPS_API_KEY` from the environment.
@@ -36,42 +36,46 @@ console.log(parcel.municipio, parcel.superficieParcela, parcel.latitud, parcel.l
36
36
 
37
37
  | Method | Endpoint | Countries |
38
38
  |--------|----------|-----------|
39
- | `parcels.get(ref, { country })` | `GET /api/catastro/{ref}` | All references (see coverage) |
40
- | `parcels.atPoint({ lat, lng, country })` | `GET /api/search/coordinates` | All, including HR, SE and UK (Scotland) |
41
- | `searchAddressCandidates(q \| { q, street, number, municipality, postcode, country, limit })` | `GET /api/search/address/candidates` | Spain (with Basque Country and Navarre) and 26 more; not SE or HR |
42
- | `parcels.findByAddress(text)` | `POST /api/search/address/parse` | Spain, central Catastro, live (prefer the candidates search) |
43
- | `getUnits(ref14, { country, previous })`, `iterUnitPages` | `GET /api/catastro/{ref14}/units` | `ES`, `PV`, `NA` |
44
- | `parcels.geometry(ref, { country })` | `GET /api/catastro/{ref}/polygon` | All but UK |
45
- | `parcels.terrain(ref, { country, lat, lng })` | `GET /api/catastro/{ref}/terrain` | All; `lat`/`lng` required outside ES, PV, NA, PT, FR, IT, DE |
46
- | `parcels.groundMotion(ref, { country, lat, lng })` | `GET /api/catastro/{ref}/ground-motion` | Same as terrain, inside EGMS coverage |
47
- | `parcels.solar`, `parcels.agriculture`, `parcels.score` | `/solar`, `/agro`, `/score` | ES, PV, NA, PT, FR, IT, DE |
48
- | `parcels.market(ref, { country })` | `/market` | Figures in FR, IT and DE (NRW); a note without figures elsewhere |
39
+ | `parcels.get(ref, { country, lang })` | `GET /v1/parcels/{ref}` | All references (see coverage); `subparcelCount` and `cropSummary` where there are crops |
40
+ | `parcels.subparcels(ref, { country, lang })` | `GET /v1/parcels/{ref}/subparcels` | ES (with Bizkaia, Álava, Navarre, Gipuzkoa), AT, EE, GR. 1 unit; 0 when there are none; `503 SUB_001` is not charged |
41
+ | `parcels.neighbors(ref, { country })` | `GET /v1/parcels/{ref}/neighbors` | Spain from the cadastre; elsewhere from our own copy (`supported: false`, free, when not) |
42
+ | `parcels.atPoint({ lat, lng, country })` | `GET /v1/search/coordinates` | All, including HR, SE and UK (Scotland) |
43
+ | `searchAddressCandidates(q \| { q, street, number, municipality, postcode, country, limit })` | `GET /v1/search/address/candidates` | Spain (with Basque Country and Navarre) and 26 more; not SE or HR |
44
+ | `parcels.findByAddress(text)` | `POST /v1/search/address/parse` | Spain, central Catastro, live (prefer the candidates search) |
45
+ | `getUnits(ref14, { country, previous })`, `iterUnitPages` | `GET /v1/parcels/{ref14}/units` | `ES`, `PV`, `NA` |
46
+ | `parcels.geometry(ref, { country })` | `GET /v1/parcels/{ref}/polygon` | All but UK |
47
+ | `parcels.terrain(ref, { country, lat, lng })` | `GET /v1/parcels/{ref}/terrain` | All; `lat`/`lng` required outside ES, PV, NA, PT, FR, IT, DE |
48
+ | `parcels.groundMotion(ref, { country, lat, lng })` | `GET /v1/parcels/{ref}/ground-motion` | Same as terrain, inside EGMS coverage |
49
+ | `parcels.solar`, `parcels.agriculture`, `parcels.score` | `/solar`, `/agriculture`, `/score` | ES, PV, NA, PT, FR, IT, DE |
50
+ | `parcels.market(ref, { country })` | `/market` | Sales in FR (DVF), IT (OMI) and DE (NRW); official province prices in `zonePrices` for ES, PV, NA; PT `available: false` |
49
51
  | `parcels.valueHistory(ref, { country })` | `/value-history` | ES, PV, NA, PT, FR, IT, DE, AT |
50
- | `parcels.compare([{ reference, country }, ...])` | `POST /api/catastro/compare` | 2 or 3 parcels in ES, PT, FR, IT, DE |
51
- | `export.file(ref, 'kml' \| 'gpx' \| 'dxf', { country })` | `GET /api/export/{format}` | Where a geometry exists |
52
- | `resolve(text, { hint })` | `GET /api/resolve` | Free, no quota: which country and kind a text is |
52
+ | `parcels.compare([{ reference, country }, ...])` | `POST /v1/parcels/compare` | 2 or 3 parcels in ES, PT, FR, IT, DE |
53
+ | `export.file(ref, 'kml' \| 'gpx', { country })` | `GET /v1/export/{format}` | Where a geometry exists |
54
+ | `locations.provinces()`, `locations.municipalities(region)`, `locations.streets({ region, municipality, name })` | `GET /v1/locations/...` | Spain (common territory): the names `findByAddress` expects |
55
+ | `cropCatalog({ lang })` | `GET /v1/reference/crops` | Free, no quota: every `cropCode` with its name in 10 languages |
56
+ | `resolve(text, { hint })` | `GET /v1/resolve` | Free, no quota: which country and kind a text is |
53
57
 
54
58
  ## Examples
55
59
 
56
60
  ```ts
57
61
  const inWarsaw = await client.parcels.atPoint({ lat: 52.2297, lng: 21.0122 });
58
- inWarsaw.referenciaCatastral;
59
- inWarsaw.pais;
62
+ inWarsaw.reference;
63
+ inWarsaw.country;
60
64
 
61
- const { candidatos } = await client.searchAddressCandidates({ q: 'Damrak 1, 1012 LG Amsterdam', country: 'NL' });
62
- candidatos[0].refCatastral;
63
- candidatos[0].confianza;
65
+ const { candidates } = await client.searchAddressCandidates({ q: 'Damrak 1, 1012 LG Amsterdam', country: 'NL' });
66
+ candidates[0].reference;
67
+ candidates[0].confidence;
64
68
 
65
69
  const terrain = await client.parcels.terrain('10194A00110004');
66
70
  terrain.relief.slope?.class;
67
- terrain.protected_areas.natura2000;
71
+ terrain.protectedAreas.natura2000;
68
72
  terrain.climate?.baseline;
69
73
 
70
74
  const brussels = await client.parcels.terrain('21004C0123/00', { country: 'BE', lat: 50.848139, lng: 4.353613 });
71
75
 
72
76
  const motion = await client.parcels.groundMotion('0745901TG4304N0002KH');
73
77
  motion.status;
74
- motion.ground_motion?.vertical.mean_mm_year;
78
+ motion.groundMotion?.vertical.meanMmYear;
75
79
 
76
80
  const comparison = await client.parcels.compare([
77
81
  { reference: '9872023VH5797S0001WX', country: 'ES' },
@@ -82,35 +86,51 @@ const kml = await client.export.file('9872023VH5797S0001WX', 'kml');
82
86
 
83
87
  const guess = await client.resolve('05102200100005');
84
88
  guess.candidates;
89
+
90
+ const crops = await client.parcels.subparcels('46063A00100121', { lang: 'en' });
91
+ crops.subparcels[0].cropCode;
92
+ crops.subparcels[0].crop;
93
+ crops.subparcels[0].cropOriginal;
94
+
95
+ const catalog = await client.cropCatalog({ lang: 'fr' });
96
+ catalog.crops.find((crop) => crop.code === 'G-')?.name;
97
+
98
+ const around = await client.parcels.neighbors('9872023VH5797S');
99
+ around.neighbors.map((n) => n.reference);
100
+
101
+ const { provinces } = await client.locations.provinces();
102
+ const { streets } = await client.locations.streets({ region: 'CIUDAD REAL', municipality: 'AGUDO', name: 'GLORIA' });
85
103
  ```
86
104
 
105
+ Crop names come in `lang` (`es`, `ca`, `en`, `pt`, `fr`, `it`, `de`, `pl`, `nl`, `cs`; English by default); the label as the source writes it is always in `cropOriginal`, and `cropCode` is stable across languages. Since 2.1.0 the parcel no longer embeds its subparcels: it carries `subparcelCount` and a `cropSummary` (crop and share of the area), and the full list with outlines is `parcels.subparcels`.
106
+
87
107
  `terrain` and `groundMotion` carry a `source.attribution` string that must be shown next to the data. A ground motion answer with `status: 'no_data'` and `reason: 'no_reflectors'` means the satellite had nothing stable to measure (fields, forest, water): there is no figure, not a zero.
88
108
 
89
- Responses are the API's `data` object, with the field names the API uses (`refCatastral`, `municipio`, `superficieParcela`…). See the [API reference](https://www.catastrogps.es/developers) and the OpenAPI spec at `https://api.parcelgps.com/api/openapi.json`.
109
+ Responses are the `data` object of the English v1 API (`https://api.parcelgps.com/v1`), typed: `reference`, `municipality`, `parcelArea`, `latitude`, `longitude`… Values that come from an official source (land use, addresses) stay in its language. See the [API reference](https://www.parcelgps.com/en/api/docs) and the OpenAPI spec at `https://api.parcelgps.com/v1/openapi.json`. Version 2 of this SDK talks only to `/v1`; 1.x used the deprecated Spanish-named `/api` routes.
90
110
 
91
111
  ## Import a building (comunidad de propietarios, Spain)
92
112
 
93
113
  Address, then finca (14-character reference), then every unit with use, area, participation coefficient, stair, floor and door.
94
114
 
95
115
  ```ts
96
- const { candidatos } = await client.searchAddressCandidates('Avenida José Rodríguez de la Borbolla Camoyán 10, Dos Hermanas');
97
- const finca = candidatos[0];
116
+ const { candidates } = await client.searchAddressCandidates('Avenida José Rodríguez de la Borbolla Camoyán 10, Dos Hermanas');
117
+ const finca = candidates[0];
98
118
 
99
- const comunidad = await client.getUnits(finca.refCatastral, { country: finca.pais as 'ES' | 'PV' | 'NA' });
100
- comunidad.totalUnidadesFinca;
101
- for (const u of comunidad.unidades) {
102
- console.log(u.refCatastral, u.uso, u.superficie, u.participacion, u.escalera, u.planta, u.puerta);
119
+ const comunidad = await client.getUnits(finca.reference, { country: finca.country as 'ES' | 'PV' | 'NA' });
120
+ comunidad.propertyUnitCount;
121
+ for (const u of comunidad.units) {
122
+ console.log(u.reference, u.use, u.area, u.ownershipShare, u.staircase, u.floor, u.door);
103
123
  }
104
124
  comunidad.dataSource;
105
125
  comunidad.attribution;
106
126
 
107
- const refreshed = await client.getUnits(finca.refCatastral, { previous: comunidad });
127
+ const refreshed = await client.getUnits(finca.reference, { previous: comunidad });
108
128
  refreshed.changed;
109
129
  ```
110
130
 
111
131
  - `getUnits` follows `nextCursor` for you (up to 200 units per page). `iterUnitPages(ref)` yields page by page.
112
132
  - **Cost: one quota unit per unit served** in each page, minimum one per page; a page is cut to the quota you have left. Pass the previous result as `previous` to re-import: unchanged pages come back as `304 Not Modified`, cost nothing and are reused; `changed` tells you if anything moved.
113
- - Basque Country and Navarre (`country: 'PV' | 'NA'`) are served from our copy of each foral cadastre: pass the foral reference of any unit or the finca key in `refCatastral`. The foral open data carry no participation coefficient.
133
+ - Basque Country and Navarre (`country: 'PV' | 'NA'`) are served from our copy of each foral cadastre: pass the foral reference of any unit or the finca key as the reference. The foral open data carry no participation coefficient.
114
134
  - Organisations with a per-finca cap agreed by contract (Startup and Growth) pay at most that cap per finca and period: see `client.lastQuota.fincaCap` (`{ cap, used }`).
115
135
 
116
136
  ## Quota, prepaid balance and limits
@@ -163,7 +183,7 @@ try {
163
183
  | `NotFoundError` | `404 NOT_FOUND` |
164
184
  | `QuotaExceededError` | `429 KEY_AUTH_004` |
165
185
  | `RateLimitError` | `429 KEY_RATE_002`, `RATE_LIMIT_EXCEEDED`; `retryAfterSeconds` |
166
- | `ServiceUnavailableError` | `503 SERVICE_UNAVAILABLE`; `retryAfterSeconds` |
186
+ | `ServiceUnavailableError` | `503 SERVICE_UNAVAILABLE`, `503 SUB_001` (subparcels source down, not charged); `retryAfterSeconds` |
167
187
  | `ServerError`, `TimeoutError`, `NetworkError` | `5xx`, client timeout, network failure |
168
188
 
169
189
  Timeouts, network errors, per-minute 429s and 502/503/504 are retried up to `maxRetries` times (default 2) with exponential backoff, waiting `Retry-After` when the API sends it (up to 60 s). An exhausted monthly quota is never retried.
@@ -173,14 +193,14 @@ Timeouts, network errors, per-minute 429s and 502/503/504 are retried up to `max
173
193
  | Option | Default | |
174
194
  |--------|---------|---|
175
195
  | `apiKey` | `process.env.CATASTROGPS_API_KEY` | Required |
176
- | `baseUrl` | `https://api.catastrogps.es` | `https://api.parcelgps.com` is the same API |
196
+ | `baseUrl` | `https://api.parcelgps.com` | Paths are under `/v1` |
177
197
  | `timeoutMs` | `30000` | Official cadastres can be slow |
178
198
  | `maxRetries` | `2` | |
179
199
  | `fetch` | `globalThis.fetch` | Inject your own for tests or proxies |
180
200
 
181
201
  ## Coverage
182
202
 
183
- Measured against production on 1 October 2026. 29 European countries, 31 official cadastres including the Basque Country and Navarre; in Croatia, Sweden and the United Kingdom the source is partial, as the table shows.
203
+ Measured against production on 1 October 2026. 29 European countries, including the Basque Country and Navarre foral cadastres: by cadastral reference in 27, by coordinates in all 29; in Croatia, Sweden and the United Kingdom the source is partial, as the table shows.
184
204
 
185
205
  | Code | Country / region | Reference | Coordinates | Address | Notes |
186
206
  |------|------------------|:---:|:---:|:---:|-------|