@chaosity/location-client 0.4.0 → 0.4.2

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.
@@ -2,6 +2,47 @@ import { GeocodeCommand, GetPlaceAdditionalFeature, GetPlaceCommand, ReverseGeoc
2
2
  import { geocodeResponseToFeatureCollection, getPlaceResponseToFeatureCollection, reverseGeocodeResponseToFeatureCollection, } from '@aws/amazon-location-utilities-datatypes';
3
3
  import debug from 'debug';
4
4
  const log = debug('location-client:geocoder');
5
+ /**
6
+ * Give the control the fields it renders, not just the ones GeoJSON needs.
7
+ *
8
+ * `@maplibre/maplibre-gl-geocoder` is a Carmen-shaped consumer: its result list
9
+ * calls `item.place_name.split(',')`, the input takes `result.place_name` on
10
+ * select, and fly-to reads `center` / `bbox`. The AWS converters produce plain
11
+ * GeoJSON — `properties` and `geometry` only — so a forward geocode used to
12
+ * hand the control features with no `place_name`. The list renderer threw, the
13
+ * control caught it and emitted `error`, and with no listener that surfaced as
14
+ * `Unhandled error. (undefined)` in the console: the Enter key silently did
15
+ * nothing. Suggestions were unaffected because that path renders `text`.
16
+ *
17
+ * The `as MaplibreGeocoderFeatureResults` cast this replaces is what hid it —
18
+ * `place_name` and `text` are required on the type.
19
+ */
20
+ function toCarmenFeatures(features) {
21
+ return features.map((feature) => {
22
+ const p = (feature.properties ?? {});
23
+ const title = typeof p.Title === 'string' ? p.Title : '';
24
+ const label = typeof p['Address.Label'] === 'string' ? p['Address.Label'] : '';
25
+ // `flattenProperties` flattens nested OBJECTS (Address.Label) but leaves an
26
+ // array of numbers intact, so MapView is still [minx, miny, maxx, maxy].
27
+ const view = p.MapView;
28
+ const bbox = Array.isArray(view) &&
29
+ view.length === 4 &&
30
+ view.every((v) => typeof v === 'number')
31
+ ? view
32
+ : undefined;
33
+ const center = feature.geometry?.type === 'Point'
34
+ ? feature.geometry.coordinates
35
+ : undefined;
36
+ return {
37
+ ...feature,
38
+ text: title || label,
39
+ place_name: label || title,
40
+ place_type: typeof p.PlaceType === 'string' ? [p.PlaceType] : [],
41
+ ...(bbox ? { bbox } : {}),
42
+ ...(center ? { center } : {}),
43
+ };
44
+ });
45
+ }
5
46
  export class GeoPlaces {
6
47
  constructor(client, map, options = {}) {
7
48
  this.client = client;
@@ -53,10 +94,14 @@ export class GeoPlaces {
53
94
  };
54
95
  }
55
96
  const response = (await this.client.send(new GeocodeCommand(commandInput)));
56
- const result = geocodeResponseToFeatureCollection(response, {
97
+ const converted = geocodeResponseToFeatureCollection(response, {
57
98
  flattenProperties: true,
58
99
  });
59
- log('forwardGeocode returned %d results', result.features?.length ?? 0);
100
+ const result = {
101
+ type: 'FeatureCollection',
102
+ features: toCarmenFeatures(converted.features),
103
+ };
104
+ log('forwardGeocode returned %d results', result.features.length);
60
105
  return result;
61
106
  }
62
107
  async reverseGeocode(config) {
@@ -70,10 +115,14 @@ export class GeoPlaces {
70
115
  Language: this.normalizeLanguage(config.language),
71
116
  };
72
117
  const response = (await this.client.send(new ReverseGeocodeCommand(commandInput)));
73
- const result = reverseGeocodeResponseToFeatureCollection(response, {
118
+ const converted = reverseGeocodeResponseToFeatureCollection(response, {
74
119
  flattenProperties: true,
75
120
  });
76
- log('reverseGeocode returned %d results', result.features?.length ?? 0);
121
+ const result = {
122
+ type: 'FeatureCollection',
123
+ features: toCarmenFeatures(converted.features),
124
+ };
125
+ log('reverseGeocode returned %d results', result.features.length);
77
126
  return result;
78
127
  }
79
128
  async getSuggestions(config) {
@@ -1,3 +1,4 @@
1
+ import { parseErrorResponse } from '../transport/errors';
1
2
  /**
2
3
  * Build a map style descriptor URL for the Location Service API.
3
4
  *
@@ -61,7 +62,19 @@ export async function fetchMapStyle(apiUrl, mapStyle, getToken, options = {}) {
61
62
  },
62
63
  });
63
64
  if (!response.ok) {
64
- throw new Error(`Failed to fetch map style: ${response.status} ${response.statusText}`);
65
+ // Read the body. The API sends `{message, code, requestId}` and the message
66
+ // is the whole point of it — for a style request it is Amazon's own
67
+ // sentence, forwarded verbatim by location-service-api#89:
68
+ //
69
+ // 400 "Traffic is not supported for style."
70
+ // 400 "light is not a supported color scheme for style Standard."
71
+ //
72
+ // This used to throw `Failed to fetch map style: 400`, discarding all of it
73
+ // two lines before anyone could read it — the same defect #89 fixed in the
74
+ // API, one layer up. Reuses parseErrorResponse so a style failure arrives as
75
+ // the same LocationServiceException as every other call in this package,
76
+ // with `code`, `statusCode` and `requestId` intact.
77
+ throw parseErrorResponse(response.status, response.statusText, await response.text(), response.headers);
65
78
  }
66
79
  const style = (await response.json());
67
80
  if (language) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chaosity/location-client",
3
- "version": "0.4.0",
3
+ "version": "0.4.2",
4
4
  "description": "Client library for Chaosity Location Service with AWS Location Service compatibility",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",