@tomtom-org/maps-sdk 0.52.0 → 0.53.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.
@@ -1 +1 @@
1
- {"version":3,"file":"services.es.js","names":[],"sources":["../src/shared/types/apiResponseErrorTypes.ts","../src/shared/errors.ts","../src/shared/schema/commonParamsSchema.ts","../src/shared/schema/validation.ts","../src/shared/serviceTemplate.ts","../src/shared/fetch.ts","../src/shared/schema/geometriesSchema.ts","../src/shared/schema/commonPlacesParamsSchema.ts","../src/search/commonSearchParamsSchema.ts","../src/along-route-search/alongRouteSearchRequestSchema.ts","../src/shared/request/requestBuildingUtils.ts","../src/shared/request/commonSearchRequestBuilder.ts","../src/along-route-search/requestBuilder.ts","../src/ev-charging-stations-availability/connectorAvailability.ts","../src/shared/ev.ts","../src/shared/geometry.ts","../src/shared/searchResultParsing.ts","../src/along-route-search/responseParser.ts","../src/along-route-search/alongRouteSearchTemplate.ts","../src/along-route-search/alongRouteSearch.ts","../src/autocomplete-search/autocompleteSearchRequestSchema.ts","../src/autocomplete-search/requestBuilder.ts","../src/autocomplete-search/responseParser.ts","../src/autocomplete-search/autocompleteSearchTemplate.ts","../src/autocomplete-search/autocompleteSearch.ts","../src/along-route-search/customize.ts","../src/autocomplete-search/customize.ts","../src/ev-charging-stations-availability/evChargingStationsAvailabilityRequestSchema.ts","../src/shared/types/commonRoutingParams.ts","../src/ev-charging-stations-availability/evChargingStationsAvailabilityResponseErrorParser.ts","../src/ev-charging-stations-availability/requestBuilder.ts","../src/ev-charging-stations-availability/responseParser.ts","../src/ev-charging-stations-availability/evChargingStationsAvailabilityTemplate.ts","../src/ev-charging-stations-availability/customize.ts","../src/shared/schema/commonGeocodeAndFuzzySearchParamsSchema.ts","../src/geocode/geocodingRequestSchema.ts","../src/shared/arrays.ts","../src/geocode/requestBuilder.ts","../src/geocode/responseParser.ts","../src/geocode/geocodingTemplate.ts","../src/geocode/customize.ts","../src/geometry-data/geometryDataRequestSchema.ts","../src/geometry-data/requestBuilder.ts","../src/geometry-data/responseParser.ts","../src/geometry-data/geometryDataTemplate.ts","../src/geometry-data/customize.ts","../src/geometry-search/geometrySearchRequestSchema.ts","../src/geometry-search/requestBuilder.ts","../src/geometry-search/responseParser.ts","../src/geometry-search/geometrySearchTemplate.ts","../src/geometry-search/geometrySearch.ts","../src/geometry-search/customize.ts","../src/place-by-id/placeByIdSchema.ts","../src/place-by-id/requestBuilder.ts","../src/place-by-id/responseParser.ts","../src/place-by-id/placeByIdTemplate.ts","../src/place-by-id/customize.ts","../src/routing/routingResponseErrorParser.ts","../src/shared/schema/vehicleParamsSchema.ts","../src/shared/schema/commonRoutingRequestSchema.ts","../src/reachable-range/types/reachableRangeParams.ts","../src/reachable-range/reachableRangeRequestSchema.ts","../src/shared/request/routingVehicleParamsBuilder.ts","../src/shared/request/commonRoutingRequestBuilder.ts","../src/reachable-range/requestBuilder.ts","../src/reachable-range/responseParser.ts","../src/reachable-range/reachableRangeTemplate.ts","../src/reachable-range/customize.ts","../src/revgeo/requestBuilder.ts","../src/shared/iso2ToIso3.ts","../src/revgeo/responseParser.ts","../src/revgeo/revGeocodeRequestSchema.ts","../src/revgeo/reverseGeocodingTemplate.ts","../src/revgeo/customize.ts","../src/routing/calculateRouteRequestSchema.ts","../src/routing/requestBuilder.ts","../src/routing/responseParser.ts","../src/routing/calculateRouteTemplate.ts","../src/routing/customize.ts","../src/traffic-area-analytics/types/trafficAreaAnalyticsParams.ts","../src/traffic-area-analytics/requestBuilder.ts","../src/traffic-area-analytics/responseParser.ts","../src/traffic-area-analytics/trafficAreaAnalyticsRequestSchema.ts","../src/traffic-area-analytics/trafficAreaAnalyticsTemplate.ts","../src/traffic-area-analytics/customize.ts","../src/traffic-incident-details/requestBuilder.ts","../src/traffic-incident-details/responseParser.ts","../src/traffic-incident-details/trafficIncidentDetailsRequestSchema.ts","../src/traffic-incident-details/trafficIncidentDetailsTemplate.ts","../src/traffic-incident-details/customize.ts","../src/customize/index.ts","../src/ev-charging-stations-availability/evChargingStationsAvailability.ts","../src/exploration-search/explorationSearchRequestSchema.ts","../src/exploration-search/requestBuilder.ts","../src/exploration-search/responseParser.ts","../src/exploration-search/explorationSearchTemplate.ts","../src/exploration-search/explorationSearch.ts","../src/exploration-search/types/areaTags.ts","../src/geocode/geocoding.ts","../src/geometry-data/geometryData.ts","../src/place-by-id/placeById.ts","../src/poi-categories/cache.ts","../src/poi-categories/poiCategoriesRequestSchema.ts","../src/poi-categories/requestBuilder.ts","../src/poi-categories/responseParser.ts","../src/poi-categories/poiCategoriesTemplate.ts","../src/poi-categories/poiCategories.ts","../src/reachable-range/calculateReachableRange.ts","../src/revgeo/reverseGeocoding.ts","../src/routing/calculateRoute.ts","../src/fuzzy-search/fuzzySearchRequestSchema.ts","../src/fuzzy-search/requestBuilder.ts","../src/fuzzy-search/responseParser.ts","../src/fuzzy-search/fuzzySearchTemplate.ts","../src/fuzzy-search/fuzzySearch.ts","../src/search/search.ts","../src/traffic-area-analytics/trafficAreaAnalytics.ts","../src/traffic-incident-details/trafficIncidentDetails.ts"],"sourcesContent":["/**\n * @ignore\n */\nexport interface BaseAPIResponseErrorBody {\n /*\n * HTTP error code.\n */\n httpStatusCode: number;\n /*\n * Detailed information about the error.\n */\n detailedError: {\n /*\n * One of the defined error codes.\n */\n code: string;\n /*\n * A human-readable representation of the error code.\n */\n message: string;\n /*\n * Target of the particular error.\n * Value: The name of the request parameter.\n */\n target: string;\n };\n}\n\n/**\n * @ignore\n */\nexport interface DefaultAPIResponseErrorBody extends BaseAPIResponseErrorBody {\n /*\n * A human-readable description of the error code.\n */\n error?: string;\n errorText?: string;\n}\n\n/**\n * @ignore\n */\nexport interface RoutingAPIResponseError {\n /*\n * \tThe format version\n */\n formatVersion: string;\n error: {\n /*\n * A human-readable representation of the error code.\n */\n description: string;\n };\n detailedError: {\n /*\n * A human-readable representation of the error code.\n */\n message: string;\n /*\n * One of the defined error codes.\n */\n code: string;\n };\n}\n\n/**\n * Error returned by an API.\n * @ignore\n */\nexport type APIErrorResponse<T = DefaultAPIResponseErrorBody> = {\n status?: number;\n message: string;\n data?: T;\n};\n\n/**\n * @ignore\n */\nexport enum APICode {\n TOO_MANY_REQUESTS = 429,\n FORBIDDEN = 403,\n}\n","import type { ZodIssue } from 'zod';\nimport type { ValidationError } from './schema/validation';\nimport type { ParseResponseError } from './serviceTypes';\nimport type { APIErrorResponse, DefaultAPIResponseErrorBody } from './types/apiResponseErrorTypes';\nimport { APICode } from './types/apiResponseErrorTypes';\nimport type { ServiceName } from './types/servicesTypes';\n\n/**\n * Base error class for all SDK-related errors.\n *\n * The SDK handles two distinct categories of errors:\n * 1. **Programming errors**: Configuration or usage errors in the user's application,\n * such as passing incorrect types to parameters or functions.\n * 2. **API errors**: Recoverable errors that occur during SDK operations,\n * such as network failures or invalid API responses.\n *\n * @example\n * ```typescript\n * try {\n * // SDK operation\n * } catch (error) {\n * if (error instanceof SDKError) {\n * console.error(`Error in ${error.service}: ${error.message}`);\n * }\n * }\n * ```\n *\n * @group Errors\n */\nexport class SDKError extends Error {\n /**\n * Creates a new SDKError instance.\n *\n * @param message - Human-readable error description\n * @param service - Name of the service where the error occurred\n * @param issues - Optional array of Zod validation issues for detailed error information\n */\n constructor(\n message: string,\n readonly service: string,\n readonly issues?: ZodIssue[],\n ) {\n super(message);\n\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, SDKError);\n }\n }\n}\n\n/**\n * Mapping of API error codes to human-readable error messages.\n *\n * Provides standardized error messages for common API response codes,\n * ensuring consistent error handling across the SDK.\n *\n * @group Errors\n */\nexport const APIErrorCode: { readonly [K in APICode as number]: string } = {\n [APICode.TOO_MANY_REQUESTS]: 'Too Many Requests: The API Key is over QPS (Queries per second)',\n [APICode.FORBIDDEN]: 'Request failed with status code 403',\n};\n\n/**\n * Error class for HTTP API response errors.\n *\n * Extends {@link SDKError} to include HTTP status codes and automatically\n * maps known error codes to user-friendly messages using {@link APIErrorCode}.\n *\n * @example\n * ```typescript\n * throw new SDKServiceError('Invalid request', 'geocoding', 400);\n * ```\n *\n * @group Errors\n */\nexport class SDKServiceError extends SDKError {\n /**\n * HTTP status code of the failed API request.\n *\n * Common values:\n * - `400`: Bad Request\n * - `403`: Forbidden\n * - `429`: Too Many Requests\n * - `500`: Internal Server Error\n */\n status?: number;\n\n /**\n * Creates a new SDKServiceError instance.\n *\n * If the status code matches a known error in {@link APIErrorCode},\n * the message will be automatically replaced with the standardized message.\n *\n * @param message - Error message from the API or custom message\n * @param service - Name of the service that generated the error\n * @param status - HTTP status code of the failed request\n */\n constructor(message: string, service: string, status?: number) {\n super(message, service);\n this.status = status;\n\n /*\n * We use as message what returns from API if any, otherwise we have our APIErrorCode as a fallback\n * Check if there is a status and if the status exists in the mapped API error types\n */\n if (this.status && APIErrorCode[this.status]) {\n this.message = APIErrorCode[this.status];\n }\n }\n}\n\n/**\n * @ignore\n * @param error\n * @param serviceName\n */\nexport const parseDefaultResponseError: ParseResponseError<DefaultAPIResponseErrorBody> = (error, serviceName) => {\n const { data, message, status } = error;\n // Different services uses property error or errorText or detailedError\n // Here we cover all situations as a default error parser\n const errorMessage = data?.error || data?.errorText || message;\n return new SDKServiceError(errorMessage, serviceName, status);\n};\n\n/**\n * @ignore\n * Generate error for APIResponse, any other error type will be returned as it is.\n * @param error The error captured by a catch function.\n * @param serviceName The name of the service.\n * @param parseResponseError\n */\nexport const buildResponseError = (\n error: unknown,\n serviceName: ServiceName,\n parseResponseError?: ParseResponseError<unknown>,\n): SDKError => {\n if ((error as APIErrorResponse).status) {\n const fetchError = error as APIErrorResponse;\n if (parseResponseError) {\n return parseResponseError(fetchError, serviceName);\n }\n return parseDefaultResponseError(fetchError, serviceName);\n }\n\n return new SDKError((error as Error).message, serviceName);\n};\n\n/**\n * @ignore\n * @param error\n * @param serviceName\n */\nexport const buildValidationError = (error: ValidationError, serviceName: ServiceName): SDKError =>\n new SDKError(error.message, serviceName, error.issues);\n","import { z } from 'zod';\n\n/**\n * @ignore\n */\nexport const commonServiceRequestSchema = z.object({\n apiKey: z.string().optional(),\n commonBaseURL: z.string().optional(),\n customServiceBaseURL: z.string().optional(),\n language: z.string().optional(),\n});\n","import type { ZodError } from 'zod';\nimport type { CommonServiceParams } from '../serviceTypes';\nimport type { RequestValidationConfig } from '../types/validation';\nimport { commonServiceRequestSchema } from './commonParamsSchema';\n\ntype ZodIssue = ZodError['issues'][number];\n\n/**\n * Format a Zod error into a human-readable string\n * @ignore\n */\nconst formatZodIssue = (issue: ZodIssue, depth = 0): string => {\n const path = issue.path.length > 0 ? `${issue.path.join('.')}: ` : '';\n if (issue.code === 'invalid_union') {\n const unionErrors = (issue as ZodIssue & { unionErrors?: ZodError[] }).unionErrors;\n if (unionErrors?.length) {\n const branches = unionErrors\n .map((unionError, i) => {\n const branchIssues = unionError.issues\n .map((nested) => formatZodIssue(nested, depth + 1))\n .join(', ');\n return `option[${i}]: { ${branchIssues} }`;\n })\n .join(' | ');\n return `${path}no matching union member (${branches})`;\n }\n }\n return `${path}${issue.message}`;\n};\n\nconst formatZodError = (error: ZodError): string => {\n return error.issues.map((issue) => formatZodIssue(issue)).join('; ');\n};\n\n/**\n * Validate Error Class for validating params input, this will be used by SDKError class.\n * @ignore\n */\nexport class ValidationError extends Error {\n issues: ZodError['issues'];\n\n constructor(zodError: ZodError) {\n const message = formatZodError(zodError);\n const issuesSummary = JSON.stringify(zodError.issues, null, 2);\n super(`${message}\\nIssues: ${issuesSummary}`);\n this.issues = zodError.issues;\n }\n}\n\n/**\n * @ignore\n * @param params\n * @param config\n */\nexport const validateRequestSchema = <T extends CommonServiceParams>(\n params: T,\n config?: RequestValidationConfig,\n): T => {\n const requestSchema = config?.schema\n ? commonServiceRequestSchema.extend(config.schema.shape)\n : commonServiceRequestSchema;\n\n // Apply all refinements using superRefine for better type compatibility in Zod v4\n const finalSchema = requestSchema.superRefine((data, ctx) => {\n // Validate common params\n if (!('commonBaseURL' in data) && !('customServiceBaseURL' in data)) {\n ctx.addIssue({\n code: 'custom',\n message: 'commonBaseURL or customServiceBaseURL is required',\n });\n }\n\n // Apply optional refinements\n if (config?.refinements?.length) {\n for (const refinement of config.refinements) {\n if (!refinement.check(data as T)) {\n ctx.addIssue({\n code: 'custom',\n message: refinement.message,\n });\n }\n }\n }\n });\n\n const validation = finalSchema.safeParse(params);\n if (!validation.success) {\n throw new ValidationError(validation.error);\n }\n\n return params;\n};\n","import { generateTomTomHeaders, mergeFromGlobal } from '@tomtom-org/maps-sdk/core';\nimport { buildResponseError, buildValidationError } from './errors';\nimport type { ValidationError } from './schema/validation';\nimport { validateRequestSchema } from './schema/validation';\nimport type { CommonServiceParams, ServiceTemplate } from './serviceTypes';\nimport type { ServiceName } from './types/servicesTypes';\n\n/**\n * @ignore\n * Template execution of a service call.\n * Any service goes through the same template steps:\n * 1- Build request\n * 2- Send request and get API response\n * 3- Parse and return API response\n * @param params The parameters for that specific service call.\n * @param template The implementation of the template steps.\n * @param serviceName The name of the service.\n */\nexport const callService = async <PARAMS extends CommonServiceParams, ApiRequest, ApiResponse, RESPONSE>(\n params: PARAMS,\n template: ServiceTemplate<PARAMS, ApiRequest, ApiResponse, RESPONSE>,\n serviceName: ServiceName,\n): Promise<RESPONSE> => {\n const customApiVersion = template.getAPIVersion?.(params);\n const mergedParams = mergeFromGlobal({ ...params, ...(customApiVersion && { apiVersion: customApiVersion }) });\n // (params.validateRequest defaults to true, thus true and undefined are the same)\n if (params.validateRequest === undefined || params.validateRequest) {\n try {\n validateRequestSchema<PARAMS>(mergedParams, template.requestValidation);\n } catch (e) {\n throw buildValidationError(e as ValidationError, serviceName);\n }\n }\n const apiRequest = template.buildRequest(mergedParams);\n const headers = generateTomTomHeaders(mergedParams);\n params.onAPIRequest?.(apiRequest);\n\n try {\n const apiResponse = await template.sendRequest(apiRequest, headers);\n params.onAPIResponse?.(apiRequest, apiResponse);\n return template.parseResponse(await apiResponse.data, mergedParams);\n } catch (e) {\n params.onAPIResponse?.(apiRequest, e);\n throw buildResponseError(e, serviceName, template.parseResponseError);\n }\n};\n","import { isProxyCredentialsMode, RetryConfig, TomTomConfig, type TomTomHeaders } from '@tomtom-org/maps-sdk/core';\nimport type { FetchInput, GetObject, ParsedFetchResponse, PostObject } from './types/fetch';\n\n/**\n * Custom error class for HTTP fetch errors.\n */\nclass FetchError extends Error {\n public readonly status: number;\n public readonly data?: unknown;\n\n constructor(status: number, message?: string, data?: unknown) {\n super(message ?? `HTTP Error ${status}`);\n this.name = 'FetchError';\n this.status = status;\n this.data = data;\n\n // Maintains proper stack trace for where our error was thrown (only available on V8)\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, FetchError);\n }\n }\n}\n\n// Returns the response as a JSON object or throws an error if the response isn't successful.\nconst returnOrThrow = async <T>(response: Response): ParsedFetchResponse<T> => {\n if (response.ok) {\n return { data: await response.json(), status: response.status };\n }\n let message: string | undefined;\n let errorBody;\n const contentType = response.headers.get('content-type');\n if (response.bodyUsed) {\n message = response.statusText;\n } else if (contentType?.includes('application/json')) {\n errorBody = await response.json();\n message = errorBody?.errorText ?? errorBody?.message ?? errorBody?.detailedError?.message;\n } else if (contentType?.includes('text/xml')) {\n errorBody = await response.text();\n message = response.statusText;\n }\n\n throw new FetchError(response.status, message, errorBody);\n};\n\n/**\n * Wraps a fetch call with automatic retry on 429 (Too Many Requests) responses.\n * Respects the Retry-After header when present, otherwise uses exponential backoff.\n * @ignore\n */\nconst fetchWithRetry = async (fetchFn: () => Promise<Response>): Promise<Response> => {\n const retryConfig = TomTomConfig.instance.get().retry;\n if (!retryConfig) {\n return fetchFn();\n }\n\n const { initialWaitMs, backoffFactor, timeoutMs } = retryConfig as Required<RetryConfig>;\n\n const startTime = Date.now();\n let backoffMs = initialWaitMs;\n let response = await fetchFn();\n\n while (response.status === 429) {\n const retryAfterSeconds = Number.parseInt(response.headers.get('retry-after') ?? '', 10);\n const waitMs = Number.isFinite(retryAfterSeconds) ? retryAfterSeconds * 1000 : backoffMs;\n\n if (Date.now() - startTime + waitMs > timeoutMs) {\n break;\n }\n\n // Drain the body to free the connection\n await response.text().catch(() => {});\n\n await new Promise((resolve) => setTimeout(resolve, waitMs));\n backoffMs *= backoffFactor;\n response = await fetchFn();\n }\n\n return response;\n};\n\nconst proxyModeCredentials = (): RequestCredentials | undefined => (isProxyCredentialsMode() ? 'include' : undefined);\n\n/**\n * Fetches the given HTTP JSON resource with an HTTP GET request and returns a promise with the response as a JSON object.\n * If the response isn't successful, it returns a rejected promise with the http error code.\n * @ignore\n * @param input A URL or a GET object with URL and optional service-specific headers.\n * @param sdkHeaders SDK-wide TomTom headers to be sent with the request.\n */\nexport const get = async <T>(input: URL | GetObject, sdkHeaders: TomTomHeaders): ParsedFetchResponse<T> =>\n returnOrThrow(\n await fetchWithRetry(() => {\n const credentials = proxyModeCredentials();\n const { url, headers: inputHeaders } = input instanceof URL ? { url: input, headers: undefined } : input;\n const headers = { ...sdkHeaders, ...inputHeaders };\n return fetch(url, credentials ? { headers, credentials } : { headers });\n }),\n );\n\n/**\n * Fetches the given HTTP JSON resource with an HTTP POST request and returns a promise with the response as a JSON object.\n * If the response isn't successful, it returns a rejected promise with the http error code.\n * @ignore\n * @param input The POST object with URL, optional payload, and optional service-specific headers.\n * @param sdkHeaders SDK-wide TomTom headers to be sent with the request.\n */\nexport const post = async <T, D>(input: PostObject<D>, sdkHeaders: TomTomHeaders): ParsedFetchResponse<T> =>\n returnOrThrow(\n await fetchWithRetry(() => {\n const credentials = proxyModeCredentials();\n const init: RequestInit = {\n method: 'POST',\n body: JSON.stringify(input.data),\n headers: { ...sdkHeaders, ...input.headers, 'Content-Type': 'application/json' },\n };\n if (credentials) init.credentials = credentials;\n\n return fetch(input.url, init);\n }),\n );\n\n/**\n * Fetches the given HTTP JSON resource with the given HTTP operation and URL/Payload as applicable.\n * * Useful for services which can use different HTTP methods depending on the parameters.\n * @param input The input object (e.g. containing either GET or POST data)\n * @param sdkHeaders SDK-wide TomTom headers to be sent with the request.\n * @ignore\n */\nexport const fetchWith = async <T, D = void>(\n input: FetchInput<D>,\n sdkHeaders: TomTomHeaders,\n): ParsedFetchResponse<T> => {\n if (input.method === 'GET') return get<T>(input, sdkHeaders);\n if (input.method === 'POST') return post<T, D>(input, sdkHeaders);\n throw new Error(`Unsupported HTTP method received: ${(input as { method: string }).method}`);\n};\n","import { z } from 'zod';\n\n/**\n * @ignore\n */\nexport const lineStringCoordsSchema = z.array(z.array(z.number()));\n\n/**\n * @ignore\n */\nexport const geometrySchema = z\n .object({\n type: z.enum([\n 'Point',\n 'MultiPoint',\n 'LineString',\n 'MultiLineString',\n 'Polygon',\n 'MultiPolygon',\n 'GeometryCollection',\n 'Circle',\n ]),\n coordinates: z.union([\n z.array(z.number()),\n lineStringCoordsSchema,\n z.array(z.array(z.array(z.number()))),\n z.array(z.array(z.array(z.array(z.number())))),\n ]),\n radius: z.optional(z.number()),\n radiusMeters: z.optional(z.number()),\n bbox: z.optional(z.array(z.number())),\n })\n .check(\n z.refine(\n (data) => (data.type === 'Circle' ? Boolean(data.radius) : true),\n 'type: \"Circle\" must have radius property',\n ),\n );\n\n/**\n * @ignore\n */\nexport const featureSchema = z.object({\n type: z.literal('Feature'),\n geometry: geometrySchema,\n id: z.optional(z.union([z.string(), z.number()])),\n properties: z.unknown().optional(),\n bbox: z.optional(z.array(z.number())),\n});\n\n/**\n * @ignore\n */\nexport const featureCollectionSchema = z.object({\n type: z.literal('FeatureCollection'),\n features: z.array(featureSchema),\n id: z.optional(z.union([z.string(), z.number()])),\n properties: z.unknown().optional(),\n bbox: z.optional(z.array(z.number())),\n});\n\n/**\n * @ignore\n */\nexport const hasLngLatSchema = z.union([\n z.tuple([z.number().min(-180).max(180), z.number().min(-90).max(90)]),\n z.tuple([z.number().min(-180).max(180), z.number().min(-90).max(90), z.number()]),\n z.object({\n type: z.literal('Point'),\n coordinates: z.array(z.number()),\n }),\n featureSchema,\n]);\n\n/**\n * @ignore\n */\nconst geoJsonbBoxSchema = z.union([\n z.array(z.number()).refine((arr) => arr.length === 4, { message: 'BBox must have 4 elements' }),\n z.array(z.number()).refine((arr) => arr.length === 6, { message: 'BBox must have 6 elements' }),\n]);\n\n/**\n * @ignore\n */\nexport const GeoJSONSchema = z.union([geometrySchema, featureSchema, featureCollectionSchema]);\n\n/**\n * @ignore\n */\nexport const hasBBoxSchema = z.union([geoJsonbBoxSchema, GeoJSONSchema, z.array(GeoJSONSchema)]);\n","import { views } from '@tomtom-org/maps-sdk/core';\nimport { z } from 'zod';\nimport { hasLngLatSchema } from './geometriesSchema';\n\n/**\n * @ignore\n */\nexport const commonPlacesParamsSchema = z.object({\n query: z.string().optional(),\n position: hasLngLatSchema.optional(),\n limit: z.number().max(100).optional(),\n extendedPostalCodesFor: z.array(z.string()).optional(),\n mapcodes: z.array(z.string()).optional(),\n view: z.enum(views).optional(),\n geographyTypes: z.array(z.string()).optional(),\n});\n","import { poiCategories } from '@tomtom-org/maps-sdk/core';\nimport { z } from 'zod';\nimport { commonPlacesParamsSchema } from '../shared/schema/commonPlacesParamsSchema';\n\nconst searchExtraParamsOptional = z.object({\n indexes: z.array(z.string()).optional(),\n poiCategories: z.array(z.enum(poiCategories)).optional(),\n poiBrands: z.array(z.string()).optional(),\n connectors: z.array(z.string()).optional(),\n fuelTypes: z.array(z.string()).optional(),\n openingHours: z.string().optional(),\n timeZone: z.string().optional(),\n relatedPois: z.string().optional(),\n minPowerKW: z.number().optional(),\n maxPowerKW: z.number().optional(),\n minFuzzyLevel: z.number().optional(),\n maxFuzzyLevel: z.number().optional(),\n});\n\n/**\n * @ignore\n */\nexport const commonSearchParamsSchema = commonPlacesParamsSchema.extend(searchExtraParamsOptional.shape);\n","import { z } from 'zod';\nimport { commonSearchParamsSchema } from '../search/commonSearchParamsSchema';\nimport { lineStringCoordsSchema } from '../shared/schema/geometriesSchema';\n\nconst lineStringSchema = z.object({\n type: z.literal('LineString'),\n coordinates: lineStringCoordsSchema,\n});\n\nconst routeFeatureSchema = z.object({\n type: z.literal('Feature'),\n geometry: lineStringSchema,\n});\n\nconst positionsArraySchema = lineStringCoordsSchema;\n\nconst routeSchema = z.union([lineStringSchema, routeFeatureSchema, positionsArraySchema]);\n\nconst alongRouteSearchRequestMandatory = z.object({\n route: routeSchema,\n maxDetourTimeSeconds: z.number().int().positive(),\n});\n\nconst alongRouteSearchRequestOptional = z.object({\n sortBy: z.enum(['detourTime', 'detourOffset']).optional(),\n});\n\n/**\n * @ignore\n */\nexport const alongRouteSearchRequestSchema = commonSearchParamsSchema.extend({\n ...alongRouteSearchRequestMandatory.shape,\n ...alongRouteSearchRequestOptional.shape,\n});\n","import type { HasLngLat, POICategory, TomTomAPIHeaders } from '@tomtom-org/maps-sdk/core';\nimport { getPosition, poiCategoriesToID } from '@tomtom-org/maps-sdk/core';\nimport { isNil } from 'lodash-es';\nimport type { CommonServiceParams } from '../serviceTypes';\n\n/**\n * Builds the TomTom API request headers common to every service: the API key,\n * API version, and language. Service request builders spread the result and add\n * any service-specific headers (e.g. `Attributes`) on top.\n * @ignore\n */\nexport const buildCommonServiceRequestHeaders = (params: CommonServiceParams): TomTomAPIHeaders => ({\n ...(params.apiKey && { 'TomTom-Api-Key': params.apiKey }),\n ...(!isNil(params.apiVersion) && { 'TomTom-Api-Version': String(params.apiVersion) }),\n ...(params.language && { 'Accept-Language': params.language }),\n});\n\n/**\n * @ignore\n * @param urlParams\n * @param params\n */\nexport const appendCommonParams = (urlParams: URLSearchParams, params: CommonServiceParams): void => {\n urlParams.append('apiVersion', String(params.apiVersion));\n if (params.apiKey) {\n urlParams.append('key', params.apiKey);\n }\n params.language && urlParams.append('language', params.language);\n};\n\n/**\n * Adds parameter from the array by repeating each array part into a query parameter of the same name.\n * E.g. ...&avoid=motorways&avoid=ferries&...\n * @ignore\n * @param urlParams\n * @param paramName\n * @param paramArray\n */\nexport const appendByRepeatingParamName = (\n urlParams: URLSearchParams,\n paramName: string,\n paramArray?: string[],\n): void => {\n for (const param of paramArray || []) {\n urlParams.append(paramName, param);\n }\n};\n\n/**\n * @ignore\n */\nexport const appendByJoiningParamValue = (\n urlParams: URLSearchParams,\n name: string,\n values?: string[] | number[] | (string | number)[],\n): void => {\n if (Array.isArray(values) && values.length > 0) {\n urlParams.append(name, values.join(','));\n }\n};\n\n/**\n * @ignore\n */\nexport const appendOptionalParam = (\n urlParams: URLSearchParams,\n name: string,\n value?: string | number | boolean,\n): void => {\n !isNil(value) && urlParams.append(name, String(value));\n};\n\n/**\n * Adds lat and lon parameters to the url.\n * @ignore\n * @param urlParams\n * @param hasLngLat\n */\nexport const appendLatLonParamsFromPosition = (urlParams: URLSearchParams, hasLngLat: HasLngLat | undefined): void => {\n const position = getPosition(hasLngLat);\n if (position) {\n urlParams.append('lat', String(position[1]));\n urlParams.append('lon', String(position[0]));\n }\n};\n\n/**\n * Map POICategory values to their numeric IDs for use in API requests.\n * @ignore\n */\nexport const mapPOICategoriesToIDs = (categories: POICategory[]): number[] =>\n categories.map((category) => poiCategoriesToID[category]);\n","import type { AlongRouteSearchParams } from '../../along-route-search';\nimport type { FuzzySearchParams } from '../../fuzzy-search';\nimport type { GeometrySearchParams } from '../../geometry-search';\nimport {\n appendByJoiningParamValue,\n appendCommonParams,\n appendLatLonParamsFromPosition,\n appendOptionalParam,\n mapPOICategoriesToIDs,\n} from './requestBuildingUtils';\n\n/**\n * @ignore\n */\nexport const PLACES_URL_PATH = '/maps/orbis/places';\n\n/**\n * Appends request parameters common to search APIs such as fuzzy + geometry search.\n * * Mutates the given searchURL with the appended parameters.\n * @param searchUrl The search URL to append parameters to. Should come without any parameters at this point.\n * @param params The search parameters, with global configuration already merged into them.\n */\nexport const appendCommonSearchParams = (\n searchUrl: URL,\n params: FuzzySearchParams | GeometrySearchParams | AlongRouteSearchParams,\n): void => {\n const urlParams = searchUrl.searchParams;\n appendCommonParams(urlParams, params);\n appendOptionalParam(urlParams, 'limit', params.limit);\n appendLatLonParamsFromPosition(urlParams, 'position' in params ? params.position : undefined);\n\n appendByJoiningParamValue(urlParams, 'fuelSet', params.fuelTypes);\n appendByJoiningParamValue(urlParams, 'idxSet', params.indexes);\n appendByJoiningParamValue(urlParams, 'brandSet', params.poiBrands);\n params.poiCategories &&\n appendByJoiningParamValue(urlParams, 'categorySet', mapPOICategoriesToIDs(params.poiCategories));\n appendByJoiningParamValue(urlParams, 'connectorSet', params.connectors);\n appendByJoiningParamValue(urlParams, 'mapcodes', params.mapcodes);\n appendByJoiningParamValue(urlParams, 'extendedPostalCodesFor', params.extendedPostalCodesFor);\n\n appendOptionalParam(urlParams, 'minPowerKW', params.minPowerKW);\n appendOptionalParam(urlParams, 'maxPowerKW', params.maxPowerKW);\n appendOptionalParam(urlParams, 'view', params.view);\n appendOptionalParam(urlParams, 'openingHours', params.openingHours);\n appendOptionalParam(urlParams, 'timeZone', params.timeZone);\n appendOptionalParam(urlParams, 'relatedPois', params.relatedPois);\n appendByJoiningParamValue(urlParams, 'entityTypeSet', params.geographyTypes);\n};\n","import type { LineString } from 'geojson';\nimport { appendCommonSearchParams, PLACES_URL_PATH } from '../shared/request/commonSearchRequestBuilder';\nimport { appendOptionalParam } from '../shared/request/requestBuildingUtils';\nimport type { AlongRouteSearchParams, AlongRouteSearchRequestAPI } from './types';\n\nconst toLineString = (route: AlongRouteSearchParams['route']): LineString => {\n if (Array.isArray(route)) {\n return { type: 'LineString', coordinates: route };\n }\n return route.type === 'Feature' ? route.geometry : route;\n};\n\nconst buildUrlBasePath = (params: AlongRouteSearchParams): string =>\n params.customServiceBaseURL ??\n `${params.commonBaseURL}${PLACES_URL_PATH}/searchAlongRoute/${params.query ?? ''}.json`;\n\n/**\n * Default function for building an along-route search request from {@link AlongRouteSearchParams}.\n * @param params The along-route search parameters, with global configuration already merged into them.\n */\nexport const buildAlongRouteSearchRequest = (params: AlongRouteSearchParams): AlongRouteSearchRequestAPI => {\n const url = new URL(buildUrlBasePath(params));\n appendCommonSearchParams(url, params);\n const urlParams = url.searchParams;\n urlParams.append('maxDetourTime', String(params.maxDetourTimeSeconds));\n appendOptionalParam(urlParams, 'sortBy', params.sortBy);\n appendOptionalParam(urlParams, 'spreadingMode', 'plan');\n\n return {\n url,\n data: {\n route: {\n points: toLineString(params.route).coordinates.map((coord) => ({\n lat: coord[1],\n lon: coord[0],\n })),\n },\n },\n };\n};\n","import type {\n ChargingPointAvailability,\n ChargingPointStatus,\n ChargingStation,\n Connector,\n ConnectorAvailability,\n ConnectorCount,\n} from '@tomtom-org/maps-sdk/core';\n\n/**\n * @ignore\n */\nexport const toChargingPointAvailability = (chargingStations: ChargingStation[]): ChargingPointAvailability => {\n const availability: ChargingPointAvailability = { count: 0, statusCounts: {} };\n for (const station of chargingStations) {\n for (const chargingPoint of station.chargingPoints) {\n availability.count++;\n availability.statusCounts[chargingPoint.status] =\n (availability.statusCounts[chargingPoint.status] || 0) + 1;\n }\n }\n return availability;\n};\n\n// Two connectors can be considered equal when they have the same type and power:\nconst areEqual = (connectorA: Connector, connectorB: Connector) =>\n connectorA.type === connectorB.type && connectorA.ratedPowerKW === connectorB.ratedPowerKW;\n\nconst addConnectorCount = (\n connectors: Connector[] | undefined,\n counts: ConnectorCount[], // we are mutating this input\n): void => {\n if (!connectors) {\n // defensive check, sometimes connectors are undefined\n return;\n }\n for (const connector of connectors) {\n const existingCount = counts.find((connectorCount) => areEqual(connector, connectorCount.connector));\n if (existingCount) {\n existingCount.count++;\n } else {\n // new count entry:\n counts.push({ connector, count: 1 });\n }\n }\n};\n\nconst addConnectorCountAndStatus = (\n connectors: Connector[] | undefined,\n status: ChargingPointStatus | undefined,\n availabilities: ConnectorAvailability[], // we are mutating this input\n): void => {\n if (!connectors) {\n // defensive check, sometimes connectors are undefined\n return;\n }\n for (const connector of connectors) {\n const existingAvailability = availabilities.find((connectorAvailability) =>\n areEqual(connector, connectorAvailability.connector),\n );\n if (existingAvailability) {\n existingAvailability.count++;\n if (status) {\n // we're mutating the input object here:\n const statusCounts = existingAvailability.statusCounts;\n existingAvailability.statusCounts[status] = (statusCounts[status] || 0) + 1;\n }\n } else {\n // new availability entry:\n availabilities.push({ connector, count: 1, statusCounts: status ? { [status]: 1 } : {} });\n }\n }\n};\n\n/**\n * @ignore\n */\nexport const toConnectorCounts = (connectors: Connector[]): ConnectorCount[] => {\n const availabilities: ConnectorAvailability[] = [];\n addConnectorCount(connectors, availabilities);\n return availabilities;\n};\n\n/**\n * @ignore\n */\nexport const toConnectorBasedAvailabilities = (chargingStations: ChargingStation[]): ConnectorAvailability[] => {\n const availabilities: ConnectorAvailability[] = [];\n for (const station of chargingStations) {\n for (const chargingPoint of station.chargingPoints) {\n addConnectorCountAndStatus(chargingPoint.connectors, chargingPoint.status, availabilities);\n }\n }\n return availabilities;\n};\n","import { ChargingSpeed } from '@tomtom-org/maps-sdk/core';\n\n/**\n * @ignore\n */\nexport const toChargingSpeed = (powerInKW: number): ChargingSpeed => {\n if (powerInKW < 12) {\n return 'slow';\n } else if (powerInKW < 50) {\n return 'regular';\n } else if (powerInKW < 150) {\n return 'fast';\n }\n return 'ultra-fast';\n};\n","import type { BBox } from '@tomtom-org/maps-sdk/core';\nimport type { Position } from 'geojson';\nimport type { BoundingBoxAPI, BoundingBoxTopLeftAPI, LatLonAPI } from './types/apiPlacesResponseTypes';\n\n/**\n * @ignore\n * @param csv\n */\nexport const csvLatLngToPosition = (csv: string): Position => {\n const splitLatLng = csv.split(',');\n return [Number(splitLatLng[1]), Number(splitLatLng[0])];\n};\n\n/**\n * @ignore\n * @param position\n */\nexport const positionToCSVLatLon = (position: Position): string => `${position[1]},${position[0]}`;\n\nconst hasTopLeftPoint = (bbox: BoundingBoxAPI): bbox is BoundingBoxTopLeftAPI => {\n return (<BoundingBoxTopLeftAPI>bbox).topLeftPoint !== undefined;\n};\n\n/**\n * @ignore\n * @param apiBBox\n */\nexport const apiToGeoJSONBBox = (apiBBox: BoundingBoxAPI): BBox => {\n let westSouth: Position;\n let eastNorth: Position;\n if (hasTopLeftPoint(apiBBox)) {\n westSouth = [apiBBox.topLeftPoint.lon, apiBBox.btmRightPoint.lat];\n eastNorth = [apiBBox.btmRightPoint.lon, apiBBox.topLeftPoint.lat];\n } else {\n westSouth = csvLatLngToPosition(apiBBox.southWest);\n eastNorth = csvLatLngToPosition(apiBBox.northEast);\n }\n return [westSouth[0], westSouth[1], eastNorth[0], eastNorth[1]];\n};\n\n/**\n * @ignore\n * @param point\n */\nexport const latLonAPIToPosition = (point: LatLonAPI): Position => {\n return [point.lon, point.lat];\n};\n","import type { Moment, OpeningHours, Place, POICategory, SearchPlaceProps, TimeRange } from '@tomtom-org/maps-sdk/core';\nimport { poiIDsToCategories, toPointGeometry } from '@tomtom-org/maps-sdk/core';\nimport { omit } from 'lodash-es';\nimport { toConnectorCounts } from '../ev-charging-stations-availability/connectorAvailability';\nimport { toChargingSpeed } from './ev';\nimport { apiToGeoJSONBBox, latLonAPIToPosition } from './geometry';\nimport type {\n BrandAPI,\n CategoryAPI,\n CommonSearchPlaceResultAPI,\n MomentAPI,\n OpeningHoursAPI,\n SummaryAPI,\n} from './types/apiPlacesResponseTypes';\nimport type { SearchSummary } from './types/searchSummary';\n\nconst parseYyyymmddDate = (dateYyyymmdd: string): { year: number; month: number; day: number } => {\n const splitDate = dateYyyymmdd.split('-');\n return {\n year: Number.parseInt(splitDate[0]),\n month: Number.parseInt(splitDate[1]),\n day: Number.parseInt(splitDate[2]),\n };\n};\n\nconst parseMoment = (momentApi: MomentAPI): Moment => {\n const { year, month, day } = parseYyyymmddDate(momentApi.date);\n return {\n dateYYYYMMDD: momentApi.date,\n year,\n month,\n day,\n hour: momentApi.hour,\n minute: momentApi.minute,\n date: new Date(year, month - 1, day, momentApi.hour, momentApi.minute),\n };\n};\n\nconst alwaysOpenInThisPeriod = (timeRanges: TimeRange[]): boolean =>\n timeRanges.length === 1 && timeRanges[0].start.hour === 0 && timeRanges[0].end.hour === 0;\n\n/**\n * @ignore\n */\nexport const parseOpeningHours = (openingHoursApi: OpeningHoursAPI): OpeningHours => {\n const timeRanges = openingHoursApi.timeRanges.map(\n (timeRangeApi): TimeRange => ({\n start: parseMoment(timeRangeApi.startTime),\n end: parseMoment(timeRangeApi.endTime),\n }),\n );\n return {\n mode: openingHoursApi.mode,\n timeRanges,\n alwaysOpenThisPeriod: alwaysOpenInThisPeriod(timeRanges),\n };\n};\n\n/**\n * Shared response parsing between geometry search and place by id service.\n * @ignore\n */\nexport const parseSearchAPIResult = (result: CommonSearchPlaceResultAPI): Place<SearchPlaceProps> => {\n const { position, entryPoints, poi, id, dist, boundingBox, chargingPark, ...rest } = result;\n const connectors = chargingPark?.connectors?.map((connector) => ({\n ...omit(connector, 'connectorType'),\n type: connector.connectorType,\n chargingSpeed: toChargingSpeed(connector.ratedPowerKW),\n }));\n return {\n type: 'Feature',\n geometry: toPointGeometry(latLonAPIToPosition(position)),\n ...(boundingBox && { bbox: apiToGeoJSONBBox(boundingBox) }),\n id,\n properties: {\n ...omit(rest, 'viewport'),\n ...(dist && { distance: dist }),\n ...(entryPoints?.length && {\n entryPoints: entryPoints.map((entrypoint) => ({\n ...entrypoint,\n position: latLonAPIToPosition(entrypoint.position),\n })),\n }),\n ...(connectors?.length && {\n chargingPark: {\n ...chargingPark,\n connectors: toConnectorCounts(connectors),\n },\n }),\n ...(poi && {\n poi: {\n name: poi.name,\n categories: (\n poi.categorySet?.map((c: CategoryAPI): POICategory => poiIDsToCategories[c.id]) ?? []\n ).filter((c: POICategory) => !!c),\n localizedCategories: poi.categories ?? [],\n ...(poi.phone && { phone: poi.phone }),\n ...(poi.brands && { brands: poi.brands.map((brand: BrandAPI) => brand.name) }),\n ...(poi.url && { url: poi.url }),\n ...(poi.openingHours && { openingHours: parseOpeningHours(poi.openingHours) }),\n ...(poi.timeZone && { timeZone: poi.timeZone }),\n },\n }),\n },\n };\n};\n\n/**\n * @ignore\n */\nexport const parseSummaryAPI = (summary: SummaryAPI): SearchSummary => {\n const { geoBias, ...rest } = summary;\n\n return {\n ...(geoBias && { geoBias: latLonAPIToPosition(geoBias) }),\n ...rest,\n };\n};\n","import { bboxFromGeoJSON, bboxOnlyIfWithArea } from '@tomtom-org/maps-sdk/core';\nimport { parseSearchAPIResult, parseSummaryAPI } from '../shared/searchResultParsing';\nimport type { AlongRouteSearchResponse, AlongRouteSearchResponseAPI } from './types';\n\n/**\n * Default function to parse an along-route search response.\n * @param apiResponse The API response.\n */\nexport const parseAlongRouteSearchResponse = (apiResponse: AlongRouteSearchResponseAPI): AlongRouteSearchResponse => {\n const features = apiResponse.results.map(parseSearchAPIResult);\n const bbox = bboxOnlyIfWithArea(bboxFromGeoJSON(features));\n return {\n type: 'FeatureCollection',\n properties: {\n ...parseSummaryAPI(apiResponse.summary),\n },\n features,\n ...(bbox && { bbox }),\n };\n};\n","import type { ServiceTemplate } from '../shared';\nimport { post } from '../shared/fetch';\nimport { alongRouteSearchRequestSchema } from './alongRouteSearchRequestSchema';\nimport { buildAlongRouteSearchRequest } from './requestBuilder';\nimport { parseAlongRouteSearchResponse } from './responseParser';\nimport type {\n AlongRouteSearchParams,\n AlongRouteSearchRequestAPI,\n AlongRouteSearchResponse,\n AlongRouteSearchResponseAPI,\n} from './types';\n\n/**\n * Along-route search service template type.\n * @ignore\n */\nexport type AlongRouteSearchTemplate = ServiceTemplate<\n AlongRouteSearchParams,\n AlongRouteSearchRequestAPI,\n AlongRouteSearchResponseAPI,\n AlongRouteSearchResponse\n>;\n\n/**\n * Along-route search service template main implementation.\n * @ignore\n */\nexport const alongRouteSearchTemplate: AlongRouteSearchTemplate = {\n requestValidation: { schema: alongRouteSearchRequestSchema },\n buildRequest: buildAlongRouteSearchRequest,\n sendRequest: post,\n parseResponse: parseAlongRouteSearchResponse,\n};\n","import { callService } from '../shared/serviceTemplate';\nimport type { AlongRouteSearchTemplate } from './alongRouteSearchTemplate';\nimport { alongRouteSearchTemplate } from './alongRouteSearchTemplate';\nimport type { AlongRouteSearchParams, AlongRouteSearchResponse } from './types';\n\n/**\n * Search for places along a route, ranked by detour cost.\n *\n * Returns POIs reachable within a given detour budget from the provided route geometry.\n * Results are sorted by detour time (or offset along the route) so the most\n * accessible stops appear first.\n *\n * @remarks\n * This service is ideal for trip planning use cases:\n * - Find fuel or EV charging stations along the way\n * - Locate food stops or rest areas near the route\n * - Surface services reachable within a time budget\n *\n * The `route` is typically sourced from a {@link calculateRoute} result.\n *\n * @param params Along-route search parameters including the route geometry and detour budget\n * @param customTemplate Advanced customization for request/response handling\n *\n * @returns Promise resolving to a collection of matching places sorted by detour cost\n *\n * @example\n * ```typescript\n * // Find coffee shops within a 5-minute detour\n * const results = await alongRouteSearch({\n * route: routeResult.features[0].geometry,\n * maxDetourTimeSeconds: 300,\n * query: 'coffee',\n * });\n *\n * // EV chargers with a 10-minute budget, sorted by position on route\n * const chargers = await alongRouteSearch({\n * route: routeResult.features[0].geometry,\n * maxDetourTimeSeconds: 600,\n * poiCategories: ['ELECTRIC_VEHICLE_STATION'],\n * sortBy: 'detourOffset',\n * });\n * ```\n *\n * @see [Along Route Search API Documentation](https://docs.tomtom.com/search-api/documentation/tomtom-orbis-maps/search-service/along-route-search)\n *\n * @ignore (exposed via 'search')\n */\nexport const alongRouteSearch = async (\n params: AlongRouteSearchParams,\n customTemplate?: Partial<AlongRouteSearchTemplate>,\n): Promise<AlongRouteSearchResponse> =>\n callService(params, { ...alongRouteSearchTemplate, ...customTemplate }, 'AlongRouteSearch');\n","import { z } from 'zod';\nimport { hasLngLatSchema } from '../shared/schema/geometriesSchema';\n\nconst autocompleteSearchRequestMandatory = z.object({\n query: z.string(),\n});\n\nconst autocompleteSearchRequestOptional = z.object({\n position: hasLngLatSchema.optional(),\n limit: z.number().max(100).optional(),\n radiusMeters: z.number().optional(),\n countries: z.array(z.string()).optional(),\n resultType: z.array(z.string()).optional(),\n});\n\n/**\n * @ignore\n */\nexport const autocompleteSearchRequestSchema = autocompleteSearchRequestMandatory.extend(\n autocompleteSearchRequestOptional.shape,\n);\n","import { PLACES_URL_PATH } from '../shared/request/commonSearchRequestBuilder';\nimport {\n appendByJoiningParamValue,\n appendCommonParams,\n appendLatLonParamsFromPosition,\n appendOptionalParam,\n} from '../shared/request/requestBuildingUtils';\nimport type { AutocompleteSearchParams } from './types';\n\nconst buildUrlBasePath = (mergedOptions: AutocompleteSearchParams): string =>\n mergedOptions.customServiceBaseURL ||\n `${mergedOptions.commonBaseURL}${PLACES_URL_PATH}/autocomplete/${mergedOptions.query}.json`;\n\n/**\n * Default function for building autocomplete request from {@link AutocompleteSearchParams}\n * @param params The autocomplete parameters, with global configuration already merged into them.\n */\nexport const buildAutocompleteSearchRequest = (params: AutocompleteSearchParams): URL => {\n const url = new URL(`${buildUrlBasePath(params)}`);\n const urlParams = url.searchParams;\n /**\n * Auto-complete service defaults the language to en-GB if not specified explicitly as service param\n * Or global config\n */\n params.language = params.language ?? 'en-GB';\n appendCommonParams(urlParams, params);\n appendOptionalParam(urlParams, 'limit', params.limit);\n appendLatLonParamsFromPosition(urlParams, params.position);\n appendByJoiningParamValue(urlParams, 'countrySet', params.countries);\n appendOptionalParam(urlParams, 'radius', params.radiusMeters);\n appendByJoiningParamValue(urlParams, 'resultSet', params.resultType);\n\n return url;\n};\n","import { poiIDsToCategories } from '@tomtom-org/maps-sdk/core';\nimport { latLonAPIToPosition } from '../shared/geometry';\nimport type {\n AutocompleteSearchCategorySegmentAPI,\n AutocompleteSearchResponse,\n AutocompleteSearchResponseAPI,\n AutocompleteSearchResultAPI,\n} from './types';\n\nconst isCategorySegment = (\n segment: AutocompleteSearchResultAPI['segments'][number],\n): segment is AutocompleteSearchCategorySegmentAPI => segment.type === 'category';\n\nconst parseResults = (results: AutocompleteSearchResultAPI[]): AutocompleteSearchResponse['results'] =>\n results.map((result) => ({\n segments: result.segments.map((segment) => {\n if (isCategorySegment(segment)) {\n const { id, ...rest } = segment;\n return { ...rest, category: poiIDsToCategories[Number(id)] };\n }\n return segment;\n }),\n }));\n\n/**\n * Default function to parse autocomplete response.\n * @param apiResponse The API response.\n */\nexport const parseAutocompleteSearchResponse = (\n apiResponse: AutocompleteSearchResponseAPI,\n): AutocompleteSearchResponse => {\n const { position, ...geoBias } = apiResponse.context.geoBias || {};\n return {\n context: {\n ...apiResponse.context,\n geoBias: {\n ...(position && { position: latLonAPIToPosition(position) }),\n radiusMeters: geoBias.radius,\n },\n },\n results: parseResults(apiResponse.results),\n };\n};\n","import type { ServiceTemplate } from '../shared';\nimport { get } from '../shared/fetch';\nimport { autocompleteSearchRequestSchema } from './autocompleteSearchRequestSchema';\nimport { buildAutocompleteSearchRequest } from './requestBuilder';\nimport { parseAutocompleteSearchResponse } from './responseParser';\nimport type { AutocompleteSearchParams, AutocompleteSearchResponse, AutocompleteSearchResponseAPI } from './types';\n\n/**\n * Autocomplete service template type.\n * @ignore\n */\nexport type AutocompleteSearchTemplate = ServiceTemplate<\n AutocompleteSearchParams,\n URL,\n AutocompleteSearchResponseAPI,\n AutocompleteSearchResponse\n>;\n\n/**\n * Autocomplete service template main implementation.\n * @ignore\n */\nexport const autocompleteSearchTemplate: AutocompleteSearchTemplate = {\n requestValidation: { schema: autocompleteSearchRequestSchema },\n buildRequest: buildAutocompleteSearchRequest,\n sendRequest: get,\n parseResponse: parseAutocompleteSearchResponse,\n};\n","import { callService } from '../shared/serviceTemplate';\nimport type { AutocompleteSearchTemplate } from './autocompleteSearchTemplate';\nimport { autocompleteSearchTemplate } from './autocompleteSearchTemplate';\nimport type { AutocompleteSearchParams, AutocompleteSearchResponse } from './types';\n\n/**\n * Autocomplete search queries as the user types, enabling faster and more accurate search.\n *\n * The Autocomplete service recognizes entities (places, addresses, POIs) within a partial\n * input query and offers them as completion suggestions. This enables real-time search\n * assistance and improves the search experience.\n *\n * @remarks\n * Key features:\n * - **Real-time suggestions**: Returns results as the user types\n * - **Entity recognition**: Identifies addresses, POIs, and geographic areas\n * - **Structured results**: Provides both plain text and structured data\n * - **Query refinement**: Helps users formulate more accurate search queries\n * - **Fast response**: Optimized for low-latency interactive use\n *\n * Typical use cases:\n * - Search box autocomplete dropdowns\n * - Address entry forms\n * - Location pickers\n * - Navigation apps\n *\n * @param params - Autocomplete parameters including the partial query\n * @param customTemplate - Advanced customization for request/response handling\n *\n * @returns Promise resolving to autocomplete suggestions\n *\n * @example\n * ```typescript\n * // Autocomplete as user types \"amster\"\n * const suggestions = await autocompleteSearch({\n * query: 'amster',\n * limit: 5\n * });\n * // Returns: Amsterdam, Amsterdam Centraal, etc.\n *\n * // Autocomplete with position bias\n * const localSuggestions = await autocompleteSearch({\n * query: 'main st',\n * position: [4.9041, 52.3676], // Near Amsterdam\n * limit: 10\n * });\n *\n * // Autocomplete restricted to a country with radius\n * const nlSuggestions = await autocompleteSearch({\n * query: 'pizz',\n * countries: ['NL'],\n * position: [4.9041, 52.3676],\n * radiusMeters: 5000\n * });\n * ```\n *\n * @see [Autocomplete API Documentation](https://docs.tomtom.com/search-api/documentation/autocomplete-service/autocomplete)\n * @see [Places Quickstart Guide](https://docs.tomtom.com/maps-sdk-js/guides/services/places/quickstart)\n * @see [Search Guide](https://docs.tomtom.com/maps-sdk-js/guides/services/places/search)\n *\n * @group Autocomplete Search\n */\nexport const autocompleteSearch = async (\n params: AutocompleteSearchParams,\n customTemplate?: Partial<AutocompleteSearchTemplate>,\n): Promise<AutocompleteSearchResponse> =>\n callService(params, { ...autocompleteSearchTemplate, ...customTemplate }, 'Autocomplete');\n","import { alongRouteSearch } from './alongRouteSearch';\nimport type { AlongRouteSearchTemplate } from './alongRouteSearchTemplate';\nimport { alongRouteSearchTemplate } from './alongRouteSearchTemplate';\nimport { buildAlongRouteSearchRequest } from './requestBuilder';\nimport { parseAlongRouteSearchResponse } from './responseParser';\n\nconst customize: {\n alongRouteSearch: typeof alongRouteSearch;\n buildAlongRouteSearchRequest: typeof buildAlongRouteSearchRequest;\n parseAlongRouteSearchResponse: typeof parseAlongRouteSearchResponse;\n alongRouteSearchTemplate: AlongRouteSearchTemplate;\n} = {\n alongRouteSearch,\n buildAlongRouteSearchRequest,\n parseAlongRouteSearchResponse,\n alongRouteSearchTemplate,\n};\nexport default customize;\n","import { autocompleteSearch } from './autocompleteSearch';\nimport type { AutocompleteSearchTemplate } from './autocompleteSearchTemplate';\nimport { autocompleteSearchTemplate } from './autocompleteSearchTemplate';\nimport { buildAutocompleteSearchRequest } from './requestBuilder';\nimport { parseAutocompleteSearchResponse } from './responseParser';\n\nconst customize: {\n autocompleteSearch: typeof autocompleteSearch;\n buildAutocompleteSearchRequest: typeof buildAutocompleteSearchRequest;\n parseAutocompleteSearchResponse: typeof parseAutocompleteSearchResponse;\n autocompleteSearchTemplate: AutocompleteSearchTemplate;\n} = {\n autocompleteSearch,\n buildAutocompleteSearchRequest,\n parseAutocompleteSearchResponse,\n autocompleteSearchTemplate,\n};\nexport default customize;\n","import { z } from 'zod';\nimport { commonServiceRequestSchema } from '../shared/schema/commonParamsSchema';\n\n/**\n * @ignore\n */\nexport const evChargingStationsAvailabilityRequestSchema = commonServiceRequestSchema.extend({\n id: z.string(),\n});\n","import type { Avoidable, HasBBox, TravelMode } from '@tomtom-org/maps-sdk/core';\nimport { VehicleParameters } from './vehicleParams';\n\n/**\n * Available route types, where each type specifies the type of optimization used when calculating routes:\n * * **fast**: Route calculation is optimized by travel time, while keeping the routes sensible. For example, the calculation may avoid shortcuts along inconvenient side roads or long detours that only save very little time.\n * * **short**: Route calculation is optimized such that a good compromise between small travel time and short travel distance is achieved.\n * * **efficient**: Route calculation is optimized such that a good compromise between small travel time and low fuel or energy consumption is achieved.\n * * **thrilling**: Route calculation is optimized such that routes include interesting or challenging roads and use as few motorways as possible.\n * There is a limit of 900km on routes planned with routeType=thrilling.\n *\n * @group Routing\n */\nexport const routeTypes = ['fast', 'short', 'efficient', 'thrilling'] as const;\n\n/**\n * Route optimization strategy for route calculation.\n *\n * Determines what the routing engine optimizes for when calculating the route.\n * Each type produces different routes suited to different use cases.\n *\n * @remarks\n * **Route Type Strategies:**\n *\n * - **`fast`**: Minimize travel time while maintaining practicality\n * - Prefers major roads and highways\n * - Avoids unnecessary detours and shortcuts on minor roads\n * - Best for most everyday use cases (commuting, business travel)\n *\n * - **`short`**: Balance between time and distance\n * - Good compromise between speed and mileage\n * - May use smaller roads to save distance\n * - Useful for short trips or when fuel costs matter\n *\n * - **`efficient`**: Minimize fuel or energy consumption\n * - Optimizes for least energy use\n * - Considers vehicle consumption model\n * - Avoids rapid acceleration/deceleration\n * - Best used with vehicle consumption parameters\n *\n * - **`thrilling`**: Scenic and engaging routes\n * - Prefers curvy, interesting roads\n * - Minimizes motorway usage\n * - **Limited to 900km maximum route length**\n * - Ideal for motorcycle rides or scenic drives\n *\n * @example\n * ```typescript\n * // Fastest route for commuting\n * const routeType: RouteType = 'fast';\n *\n * // Most fuel-efficient route for long trip\n * const ecoRoute: RouteType = 'efficient';\n *\n * // Scenic route for leisure\n * const scenicRoute: RouteType = 'thrilling';\n * ```\n *\n * @group Routing\n */\nexport type RouteType = (typeof routeTypes)[number];\n\n/**\n * Traffic consideration mode for route calculation.\n *\n * Controls how traffic conditions are factored into routing and travel time estimates.\n *\n * @remarks\n * **Traffic Modes:**\n *\n * - **`live`**: Real-time + historical traffic\n * - Includes current traffic jams and incidents\n * - Considers short-term and long-term road closures\n * - Most accurate for immediate departures\n * - Updates with current conditions\n *\n * - **`historical`**: Typical traffic patterns only\n * - Based on historical data for time of day/week\n * - Ignores current traffic conditions\n * - Good for future trip planning\n * - More predictable for scheduled departures\n *\n * @example\n * ```typescript\n * // Route considering current traffic (departing now)\n * const trafficMode: TrafficInput = 'live';\n *\n * // Route based on typical patterns (planning ahead)\n * const plannedRoute: TrafficInput = 'historical';\n * ```\n *\n * @group Routing\n */\nexport type TrafficInput = 'live' | 'historical';\n\n/**\n * Cost model criteria for route optimization.\n *\n * Defines routing preferences and constraints that influence path selection.\n * Combines route type, traffic consideration, and avoidance criteria to determine\n * what makes a route \"better\" in the eyes of the routing engine.\n *\n * @remarks\n * The cost model affects how the route path is calculated, but does not affect what other data the response includes.\n *\n * The cost model balances multiple factors:\n * - Time efficiency\n * - Distance\n * - Fuel/energy consumption\n * - User preferences (avoid tolls, ferries, etc.)\n * - Traffic conditions\n *\n * @example\n * ```typescript\n * // Fast route avoiding tolls\n * const costModel: CostModel = {\n * routeType: 'fast',\n * traffic: 'live',\n * avoid: ['tollRoads']\n * };\n *\n * // Eco-friendly route avoiding highways\n * const ecoCostModel: CostModel = {\n * routeType: 'efficient',\n * traffic: 'historical',\n * avoid: ['motorways', 'ferries']\n * };\n *\n * // Scenic route for leisure\n * const scenicCostModel: CostModel = {\n * routeType: 'thrilling',\n * avoid: ['motorways', 'tollRoads']\n * };\n * ```\n *\n * @group Routing\n */\nexport type CostModel = {\n /**\n * Specifies something that the route calculation should try to avoid when determining the route.\n * @default None\n */\n avoid?: Avoidable[];\n\n /**\n * Rectangular areas for the routing engine to bypass, each expressed as a {@link HasBBox}.\n *\n * Up to 10 rectangles are supported. Useful for avoiding construction zones,\n * restricted areas, or known congestion points.\n *\n * @remarks\n * Constraints per rectangle:\n * - Maximum size: ~160×160 km\n * - Cannot cross the 180th meridian\n * - Latitude must be between −80° and +80°\n *\n * @example\n * ```typescript\n * // Avoid an area using a BBox array [west, south, east, north]\n * avoidAreas: [\n * [2.265938, 48.81851, 2.41115, 48.90309]\n * ]\n * ```\n */\n avoidAreas?: HasBBox[];\n\n /**\n * Decides how traffic is considered for computing routes.\n *\n * Possible values are:\n * * live: In addition to historical travel times, routing and estimated travel time\n * consider traffic jams and short- and long-term closures during the travel time window.\n * * historical: Routing and estimated travel time consider historical travel times and long term closures.\n * Traffic jams and short-term closures during the travel time window do not influence routing or travel time.\n *\n * @default live\n *\n * @remarks\n * This setting does not affect whether live traffic is included in the response or not.\n * * Considering historical traffic in cost model does not mean live traffic is not included in the response.\n */\n traffic?: TrafficInput;\n\n /**\n * Specifies the type of optimization used when calculating routes.\n * Possible values are:\n *\n * * **fast**: Route calculation is optimized by travel time, while keeping the routes sensible. For example, the calculation may avoid shortcuts along inconvenient side roads or long detours that only save very little time.\n * * **short**: Route calculation is optimized such that a good compromise between small travel time and short travel distance is achieved.\n * * **efficient**: Route calculation is optimized such that a good compromise between small travel time and low fuel or energy consumption is achieved.\n * * **thrilling**: Route calculation is optimized such that routes include interesting or challenging roads and use as few motorways as possible.\n * There is a limit of 900km on routes planned with routeType=thrilling.\n * @default fast\n */\n routeType?: RouteType;\n};\n\ntype DepartArriveOption = 'departAt' | 'arriveBy';\n\n/**\n * Departure or arrival time specification for route planning.\n *\n * Allows specifying either when to depart from the origin or when to arrive at the destination.\n * The routing engine calculates the route optimized for that specific time window, considering\n * traffic patterns for that time of day.\n *\n * @typeParam Option - Whether this specifies departure or arrival time\n *\n * @remarks\n * Traffic conditions vary significantly by:\n * - Time of day (rush hour vs off-peak)\n * - Day of week (weekday vs weekend)\n * - Special events or holidays\n *\n * Specifying a departure or arrival time enables the router to:\n * - Use appropriate traffic data for that time\n * - Plan around rush hour or quiet periods\n * - Calculate accurate arrival/departure times\n * - Account for time-dependent road restrictions\n *\n * **Important Notes:**\n * - Times are processed in the timezone of the origin/destination\n *\n * @example\n * ```typescript\n * // Depart at specific time (morning commute)\n * const departParams: DepartArriveParams = {\n * option: 'departAt',\n * date: new Date('2025-10-20T08:00:00Z')\n * };\n *\n * // Arrive by specific time (catch a flight)\n * const arriveParams: DepartArriveParams = {\n * option: 'arriveBy',\n * date: new Date('2025-10-20T14:00:00Z')\n * };\n *\n * // Plan route avoiding rush hour\n * const offPeakDepart: DepartArriveParams = {\n * option: 'departAt',\n * date: new Date('2025-10-20T10:30:00Z') // After morning rush\n * };\n * ```\n *\n * @group Routing\n */\nexport type DepartArriveParams<Option extends DepartArriveOption = DepartArriveOption> = {\n /**\n * Whether to specify a departure or arrival time.\n *\n * @remarks\n * - `departAt`: Calculate route from this departure time forward\n * - `arriveBy`: Calculate route backward to arrive by this time\n */\n option: Option;\n\n /**\n * The date and time to depart or arrive.\n *\n * @remarks\n * If past dates are supplied or dates that are impossible to achieve\n * (e.g., an imminent arrival date for a very long route), the system\n * will default to departing immediately.\n *\n * Times should be specified in ISO 8601 format or as JavaScript Date objects.\n */\n date: Date;\n};\n\n/**\n * Common parameters shared across all routing service requests.\n *\n * These parameters configure how routes are calculated, including optimization\n * strategy, vehicle characteristics, timing constraints, and travel preferences.\n * They provide a consistent interface across different routing services.\n *\n * @remarks\n * Most routing services (calculateRoute, calculateMatrixRoute, calculateReachableRange)\n * accept these parameters to customize route calculation. They control:\n * - What to optimize for (time, distance, fuel)\n * - Vehicle constraints and capabilities\n * - When to travel (affecting traffic)\n * - What features to avoid\n * - Mode of transportation\n *\n * **Service Compatibility:**\n * - {@link calculateRoute}: All parameters supported\n * - {@link calculateMatrixRoute}: Subset of parameters\n * - {@link calculateReachableRange}: Subset of parameters\n *\n * @example\n * ```typescript\n * // Standard car route avoiding tolls\n * const routingParams: CommonRoutingParams = {\n * costModel: {\n * routeType: 'fast',\n * traffic: 'live',\n * avoid: ['tollRoads']\n * },\n * travelMode: 'car',\n * when: {\n * option: 'departAt',\n * date: new Date('2025-10-20T08:00:00Z')\n * }\n * };\n *\n * // Electric vehicle route with consumption model\n * const evRoutingParams: CommonRoutingParams = {\n * costModel: {\n * routeType: 'efficient',\n * traffic: 'live'\n * },\n * travelMode: 'car',\n * vehicle: {\n * engineType: 'electric',\n * model: {\n * dimensions: {\n * weightKG: 2000\n * },\n * engine: {\n * consumption: {\n * speedToConsumption: [\n * { speedKMH: 50, consumptionUnitsPer100KM: 15 },\n * { speedKMH: 90, consumptionUnitsPer100KM: 18 },\n * { speedKMH: 120, consumptionUnitsPer100KM: 22 }\n * ]\n * }\n * }\n * },\n * state: {\n * currentChargeInkWh: 60\n * }\n * }\n * };\n *\n * // Truck route with restrictions\n * const truckParams: CommonRoutingParams = {\n * costModel: {\n * routeType: 'short',\n * avoid: ['tollRoads', 'ferries']\n * },\n * travelMode: 'truck',\n * vehicle: {\n * model: {\n * dimensions: {\n * weightKG: 40000\n * }\n * },\n * restrictions: {\n * maxSpeedKMH: 90\n * }\n * }\n * };\n * ```\n *\n * @see [Common Routing Parameters Documentation](https://docs.tomtom.com/routing-api/documentation/routing/common-routing-parameters)\n *\n * @group Routing\n */\nexport type CommonRoutingParams = {\n /**\n * Criteria that specifies what paths to prefer during routing.\n */\n costModel?: CostModel;\n\n /**\n * The primary means of transportation to be used while routing.\n *\n * The travel mode for the requested route.\n * Note that the requested travelMode may not be available for the entire route. Where\n * the requested travelMode is not available for a particular section, the element of the\n * response for that section will be 'other'.\n * @default None\n */\n travelMode?: TravelMode;\n\n /**\n * Specifies when to depart or arrive.\n * If past dates are supplied or in a way that are impossible to achieve\n * (e.g. an imminent arrival date for a long route), then it will default to departing now.\n * @default depart now\n */\n when?: DepartArriveParams;\n\n /**\n * Parameters for the vehicle that will be used to drive the route.\n */\n vehicle?: VehicleParameters;\n\n /**\n * The version of the API to use.\n * * The SDK will use the right default when not specified.\n * * Use it only if you really need to target a specific API version.\n */\n apiVersion?: number;\n};\n","import type { ParseResponseError } from '../shared';\nimport { SDKServiceError } from '../shared';\n\n/**\n * @ignore\n * @param apiError\n * @param serviceName\n */\nexport const parseEVChargingStationsAvailabilityResponseError: ParseResponseError = (apiError, serviceName) => {\n const errorMessage = apiError.data?.detailedError?.message ?? apiError.message;\n return new SDKServiceError(errorMessage, serviceName, apiError.status);\n};\n","import { PLACES_URL_PATH } from '../shared/request/commonSearchRequestBuilder';\nimport { appendCommonParams } from '../shared/request/requestBuildingUtils';\nimport type { ChargingStationsAvailabilityParams } from './types/evChargingStationsAvailabilityParams';\n\nconst buildUrlBasePath = (params: ChargingStationsAvailabilityParams): string =>\n params.customServiceBaseURL ?? `${params.commonBaseURL}${PLACES_URL_PATH}/ev/id`;\n\n/**\n * Default method for building ev charging stations availability request from {@link ChargingStationsAvailabilityParams}\n * @param params The charging availability parameters, with global configuration already merged into them.\n */\nexport const buildEVChargingStationsAvailabilityRequest = (params: ChargingStationsAvailabilityParams): URL => {\n const url = new URL(buildUrlBasePath(params));\n const urlParams = url.searchParams;\n appendCommonParams(urlParams, params);\n urlParams.append('id', params.id);\n return url;\n};\n","import type { ChargingStation, ChargingStationsAvailability } from '@tomtom-org/maps-sdk/core';\nimport { toChargingSpeed } from '../shared/ev';\nimport { parseOpeningHours } from '../shared/searchResultParsing';\nimport { toChargingPointAvailability, toConnectorBasedAvailabilities } from './connectorAvailability';\nimport type { ChargingStationsAvailabilityResponseAPI } from './types/apiTypes';\n\nconst toChargingPointStations = (stations: ChargingStation[]) =>\n stations.map((station) => ({\n ...station,\n chargingPoints: station.chargingPoints.map((chargingPoint) => ({\n ...chargingPoint,\n connectors: chargingPoint.connectors?.map((connector) => ({\n ...connector,\n chargingSpeed: toChargingSpeed(connector.ratedPowerKW),\n })),\n })),\n }));\n\n/**\n * Default method for parsing ev charging stations availability from {@link ChargingStationsAvailability}\n * @param apiResponse\n */\nexport const parseEVChargingStationsAvailabilityResponse = (\n apiResponse: ChargingStationsAvailabilityResponseAPI,\n): ChargingStationsAvailability | undefined => {\n const result = apiResponse.results?.[0];\n return result\n ? {\n id: result.id,\n accessType: result.accessType,\n chargingStations: toChargingPointStations(result.chargingStations),\n chargingPointAvailability: toChargingPointAvailability(result.chargingStations),\n connectorAvailabilities: toConnectorBasedAvailabilities(result.chargingStations),\n ...(result.openingHours && { openingHours: parseOpeningHours(result.openingHours) }),\n }\n : undefined;\n};\n","import type { ChargingStationsAvailability } from '@tomtom-org/maps-sdk/core';\nimport type { ServiceTemplate } from '../shared';\nimport { get } from '../shared/fetch';\nimport { evChargingStationsAvailabilityRequestSchema } from './evChargingStationsAvailabilityRequestSchema';\nimport { parseEVChargingStationsAvailabilityResponseError } from './evChargingStationsAvailabilityResponseErrorParser';\nimport { buildEVChargingStationsAvailabilityRequest } from './requestBuilder';\nimport { parseEVChargingStationsAvailabilityResponse } from './responseParser';\nimport type { ChargingStationsAvailabilityResponseAPI } from './types/apiTypes';\nimport type { ChargingStationsAvailabilityParams } from './types/evChargingStationsAvailabilityParams';\n\n/**\n * EV Charging Stations Availability service template type.\n * @ignore\n */\nexport type EVChargingStationsAvailabilityTemplate = ServiceTemplate<\n ChargingStationsAvailabilityParams,\n URL,\n ChargingStationsAvailabilityResponseAPI,\n ChargingStationsAvailability | undefined\n>;\n\n/**\n * EV Charging Stations Availability service template main implementation.\n * @ignore\n */\nexport const evChargingStationsAvailabilityTemplate: EVChargingStationsAvailabilityTemplate = {\n requestValidation: { schema: evChargingStationsAvailabilityRequestSchema },\n buildRequest: buildEVChargingStationsAvailabilityRequest,\n sendRequest: get,\n parseResponse: parseEVChargingStationsAvailabilityResponse,\n parseResponseError: parseEVChargingStationsAvailabilityResponseError,\n};\n","import type { EVChargingStationsAvailabilityTemplate } from './evChargingStationsAvailabilityTemplate';\nimport { evChargingStationsAvailabilityTemplate } from './evChargingStationsAvailabilityTemplate';\nimport { buildEVChargingStationsAvailabilityRequest } from './requestBuilder';\nimport { parseEVChargingStationsAvailabilityResponse } from './responseParser';\n\nconst customize: {\n buildEVChargingStationsAvailabilityRequest: typeof buildEVChargingStationsAvailabilityRequest;\n parseEVChargingStationsAvailabilityResponse: typeof parseEVChargingStationsAvailabilityResponse;\n evChargingStationsAvailabilityTemplate: EVChargingStationsAvailabilityTemplate;\n} = {\n buildEVChargingStationsAvailabilityRequest,\n parseEVChargingStationsAvailabilityResponse,\n evChargingStationsAvailabilityTemplate,\n};\nexport default customize;\n","import { z } from 'zod';\nimport { hasBBoxSchema } from './geometriesSchema';\n\n/**\n * @ignore\n */\nexport const commonGeocodeAndFuzzySearchParamsSchema = z.object({\n typeahead: z.boolean().optional(),\n offset: z.number().max(1900).optional(),\n radiusMeters: z.number().optional(),\n boundingBox: hasBBoxSchema.optional(),\n countries: z.array(z.string()).optional(),\n});\n","import { z } from 'zod';\nimport { commonGeocodeAndFuzzySearchParamsSchema } from '../shared/schema/commonGeocodeAndFuzzySearchParamsSchema';\nimport { commonServiceRequestSchema } from '../shared/schema/commonParamsSchema';\nimport { commonPlacesParamsSchema } from '../shared/schema/commonPlacesParamsSchema';\n\n/**\n * @ignore\n */\nexport const geocodingRequestSchema = commonServiceRequestSchema\n .extend(commonPlacesParamsSchema.shape)\n .extend(commonGeocodeAndFuzzySearchParamsSchema.shape)\n .extend({ query: z.string() });\n","/**\n * @ignore\n * @param input\n */\nexport const arrayToCSV = (input: unknown | unknown[]): string =>\n !input ? '' : Array.isArray(input) ? input.join(',') : typeof input === 'string' ? input : String(input);\n\n/**\n * Samples, if necessary, the given array to fit within the given max length.\n * * If the array already fits within maxLength, the same array is returned.\n * * The sampling is done by spreading the array points at a constant increment.\n * * The first and last points are always included if array and max lengths are > 1.\n * * The sampling is done with speed in mind, so it's not guaranteed to always fit maxLength. Very often it will be shorter.\n *\n * Use this when needing to quickly simplify an array to fit some max length without caring too much on the lost detail.\n * * If both the array and max length are long enough, the loss of detail won't likely be an issue.\n * @ignore\n */\nexport const sampleWithinMaxLength = <T>(array: T[], maxLength: number): T[] => {\n const length = array.length;\n if (length <= maxLength) {\n return array;\n }\n\n const sampledArray = [];\n let i;\n const increment = Math.ceil(length / maxLength);\n for (i = 0; i < length; i += increment) {\n sampledArray.push(array[i]);\n }\n // ensuring the last point is always added:\n if (maxLength > 1 && i >= length - increment) {\n if (sampledArray.length < maxLength) {\n sampledArray.push(array[length - 1]);\n } else {\n sampledArray[sampledArray.length - 1] = array[length - 1];\n }\n }\n\n return sampledArray;\n};\n","import { bboxFromGeoJSON } from '@tomtom-org/maps-sdk/core';\nimport { isNil } from 'lodash-es';\nimport { arrayToCSV } from '../shared/arrays';\nimport { PLACES_URL_PATH } from '../shared/request/commonSearchRequestBuilder';\nimport { appendCommonParams, appendLatLonParamsFromPosition } from '../shared/request/requestBuildingUtils';\nimport type { GeocodingParams } from './types/geocodingParams';\n\nconst buildUrlBasePath = (params: GeocodingParams): string =>\n params.customServiceBaseURL || `${params.commonBaseURL}${PLACES_URL_PATH}/geocode`;\n\n/**\n * Default method for building geocoding request from {@link GeocodingParams}\n * @param params The geocoding parameters, with global configuration already merged into them.\n */\nexport const buildGeocodingRequest = (params: GeocodingParams): URL => {\n const url = new URL(`${buildUrlBasePath(params)}/${params.query}.json`);\n const urlParams = url.searchParams;\n appendCommonParams(urlParams, params);\n // geocoding specific parameters:\n params.typeahead && urlParams.append('typeahead', String(params.typeahead));\n !isNil(params.limit) && urlParams.append('limit', String(params.limit));\n !isNil(params.offset) && urlParams.append('ofs', String(params.offset));\n appendLatLonParamsFromPosition(urlParams, params.position);\n params.countries && urlParams.append('countrySet', arrayToCSV(params.countries));\n !isNil(params.radiusMeters) && urlParams.append('radius', String(params.radiusMeters));\n const bbox = params.boundingBox && bboxFromGeoJSON(params.boundingBox);\n if (bbox) {\n urlParams.append('topLeft', arrayToCSV([bbox[3], bbox[0]]));\n urlParams.append('btmRight', arrayToCSV([bbox[1], bbox[2]]));\n }\n params.extendedPostalCodesFor &&\n urlParams.append('extendedPostalCodesFor', arrayToCSV(params.extendedPostalCodesFor));\n params.mapcodes && urlParams.append('mapcodes', arrayToCSV(params.mapcodes));\n params.view && urlParams.append('view', params.view);\n params.geographyTypes && urlParams.append('entityTypeSet', arrayToCSV(params.geographyTypes));\n return url;\n};\n","import type { GeographyType, Place } from '@tomtom-org/maps-sdk/core';\nimport { bboxFromGeoJSON, bboxOnlyIfWithArea, toPointGeometry } from '@tomtom-org/maps-sdk/core';\nimport { omit } from 'lodash-es';\nimport { apiToGeoJSONBBox, latLonAPIToPosition } from '../shared/geometry';\nimport type { GeocodingResponseAPI, GeocodingResultAPI } from './types/apiTypes';\nimport type { GeocodingProps, GeocodingResponse } from './types/geocodingResponse';\n\nconst parseApiResult = (result: GeocodingResultAPI): Place<GeocodingProps> => {\n const { position, boundingBox, dist, entryPoints, addressRanges, entityType, id, ...rest } = result;\n\n return {\n type: 'Feature',\n geometry: toPointGeometry(latLonAPIToPosition(position)),\n ...(boundingBox && { bbox: apiToGeoJSONBBox(boundingBox) }),\n id,\n properties: {\n ...omit(rest, 'viewport'),\n ...(dist && { distance: dist }),\n ...(entityType && { geographyType: entityType.split(',') as GeographyType[] }),\n ...(entryPoints && {\n entryPoints: entryPoints.map((entrypoint) => ({\n ...entrypoint,\n position: latLonAPIToPosition(entrypoint.position),\n })),\n }),\n ...(addressRanges && {\n addressRanges: {\n ...addressRanges,\n from: latLonAPIToPosition(addressRanges.from),\n to: latLonAPIToPosition(addressRanges.to),\n },\n }),\n },\n };\n};\n\n/**\n * Default method for parsing geocoding request from {@link GeocodingResponse}\n * @param apiResponse\n */\nexport const parseGeocodingResponse = (apiResponse: GeocodingResponseAPI): GeocodingResponse => {\n const results = apiResponse.results;\n const features = results.map(parseApiResult);\n const bbox = bboxOnlyIfWithArea(bboxFromGeoJSON(features));\n return {\n type: 'FeatureCollection',\n features,\n ...(bbox && { bbox }),\n };\n};\n","import type { ServiceTemplate } from '../shared';\nimport { get } from '../shared/fetch';\nimport { geocodingRequestSchema } from './geocodingRequestSchema';\nimport { buildGeocodingRequest } from './requestBuilder';\nimport { parseGeocodingResponse } from './responseParser';\nimport type { GeocodingResponseAPI } from './types/apiTypes';\nimport type { GeocodingParams } from './types/geocodingParams';\nimport type { GeocodingResponse } from './types/geocodingResponse';\n\n/**\n * Geocoding service template type.\n * @ignore\n */\nexport type GeocodingTemplate = ServiceTemplate<GeocodingParams, URL, GeocodingResponseAPI, GeocodingResponse>;\n\n/**\n * Geocoding service template main implementation.\n * @ignore\n */\nexport const geocodingTemplate: GeocodingTemplate = {\n requestValidation: { schema: geocodingRequestSchema },\n buildRequest: buildGeocodingRequest,\n sendRequest: get,\n parseResponse: parseGeocodingResponse,\n};\n","import type { GeocodingTemplate } from './geocodingTemplate';\nimport { geocodingTemplate } from './geocodingTemplate';\nimport { buildGeocodingRequest } from './requestBuilder';\nimport { parseGeocodingResponse } from './responseParser';\n\nconst customize: {\n buildGeocodingRequest: typeof buildGeocodingRequest;\n parseGeocodingResponse: typeof parseGeocodingResponse;\n geocodingTemplate: GeocodingTemplate;\n} = {\n buildGeocodingRequest,\n parseGeocodingResponse,\n geocodingTemplate,\n};\nexport default customize;\n","import { z } from 'zod';\nimport { featureCollectionSchema, featureSchema } from '../shared/schema/geometriesSchema';\n\nconst geometryDataRequestMandatory = z.object({\n geometries: z.union([\n featureCollectionSchema,\n z\n .array(z.union([z.string(), featureSchema]))\n .min(1)\n .max(20),\n ]),\n});\n\nconst geometryDataRequestOptional = z.object({\n zoom: z.number().min(0).max(22).optional(),\n});\n\nexport const geometryDataRequestSchema = geometryDataRequestMandatory.extend(geometryDataRequestOptional.shape);\n","import type { Place, Places } from '@tomtom-org/maps-sdk/core';\nimport { arrayToCSV } from '../shared/arrays';\nimport { PLACES_URL_PATH } from '../shared/request/commonSearchRequestBuilder';\nimport { appendOptionalParam } from '../shared/request/requestBuildingUtils';\nimport type { GeometriesInput, GeometryParams } from './types/geometryDataParams';\n\nconst buildUrlBasePath = (params: GeometryParams): string =>\n params.customServiceBaseURL || `${params.commonBaseURL}${PLACES_URL_PATH}/additionalData.json`;\n\nconst getGeometryIDs = (placesArray: Place[]): string[] =>\n placesArray.map((place) => place.properties.dataSources?.geometry?.id as string).filter((id) => id);\n\n// (@see geometryDataRequestSchema)\nconst appendGeometries = (urlParams: URLSearchParams, geometries: GeometriesInput | Places | Place[]): void => {\n let geometryIDs: string[];\n\n if (Array.isArray(geometries)) {\n // (assuming min and max length already validated)\n if (typeof geometries[0] === 'string') {\n geometryIDs = geometries as string[];\n } else {\n geometryIDs = getGeometryIDs(geometries as Place[]);\n }\n } else {\n // (assuming already validated as FeatureCollection)\n geometryIDs = getGeometryIDs(geometries.features);\n }\n\n urlParams.append('geometries', arrayToCSV(geometryIDs));\n};\n\n/**\n * Default function for building a geometry data request from {@link GeometryDataParams}\n * @param params The geometry data parameters, with global configuration already merged into them.\n */\nexport const buildGeometryDataRequest = (params: GeometryParams): URL => {\n const url = new URL(buildUrlBasePath(params));\n const urlParams = url.searchParams;\n // (no language in this service)\n urlParams.append('apiVersion', String(params.apiVersion));\n if (params.apiKey) {\n urlParams.append('key', params.apiKey);\n }\n appendGeometries(urlParams, params.geometries);\n appendOptionalParam(urlParams, 'geometriesZoom', params.zoom);\n return url;\n};\n","import { bboxFromGeoJSON, generateId, PolygonFeature, PolygonFeatures } from '@tomtom-org/maps-sdk/core';\nimport type { GeometryDataResponseAPI } from './types/apiTypes';\n\n/**\n * Default geometry data API response parsing.\n * * The API response consists of an array, with a FeatureCollection (with only one feature) for each geometry.\n * * The parsed response consists of a fully-GeoJSON-compatible FeatureCollection with a Feature for each geometry.\n * * Each geometry ID is included in each GeoJSON feature \"id\" field.\n * @param apiResponse\n */\nexport const parseGeometryDataResponse = (apiResponse: GeometryDataResponseAPI): PolygonFeatures => {\n const features = apiResponse.additionalData\n .flatMap((data) =>\n (data.geometryData as PolygonFeatures)?.features.map((feature) => ({\n ...feature,\n id: feature.id ?? generateId(),\n bbox: bboxFromGeoJSON(feature.geometry),\n })),\n )\n .filter((feature) => feature) as PolygonFeature[];\n return {\n type: 'FeatureCollection',\n bbox: bboxFromGeoJSON(features),\n features,\n };\n};\n","import type { PolygonFeatures } from '@tomtom-org/maps-sdk/core';\nimport type { ServiceTemplate } from '../shared';\nimport { get } from '../shared/fetch';\nimport { geometryDataRequestSchema } from './geometryDataRequestSchema';\nimport { buildGeometryDataRequest } from './requestBuilder';\nimport { parseGeometryDataResponse } from './responseParser';\nimport type { GeometryDataResponseAPI } from './types/apiTypes';\nimport type { GeometryParams } from './types/geometryDataParams';\n\n/**\n * @ignore\n */\nexport type GeometryDataTemplate = ServiceTemplate<GeometryParams, URL, GeometryDataResponseAPI, PolygonFeatures>;\n\n/**\n * @ignore\n */\nexport const geometryDataTemplate: GeometryDataTemplate = {\n requestValidation: { schema: geometryDataRequestSchema },\n buildRequest: buildGeometryDataRequest,\n sendRequest: get,\n parseResponse: parseGeometryDataResponse,\n};\n","import type { GeometryDataTemplate } from './geometryDataTemplate';\nimport { geometryDataTemplate } from './geometryDataTemplate';\nimport { buildGeometryDataRequest } from './requestBuilder';\nimport { parseGeometryDataResponse } from './responseParser';\n\nconst customize: {\n buildGeometryDataRequest: typeof buildGeometryDataRequest;\n parseGeometryDataResponse: typeof parseGeometryDataResponse;\n geometryDataTemplate: GeometryDataTemplate;\n} = {\n buildGeometryDataRequest,\n parseGeometryDataResponse,\n geometryDataTemplate,\n};\nexport default customize;\n","import { z } from 'zod';\nimport { commonSearchParamsSchema } from '../search/commonSearchParamsSchema';\nimport { featureCollectionSchema, geometrySchema } from '../shared/schema/geometriesSchema';\n\nconst geometrySearchRequestMandatory = z.object({\n geometries: z.array(z.union([featureCollectionSchema, geometrySchema])),\n});\n\n/**\n * @ignore\n */\nexport const geometrySearchRequestSchema = commonSearchParamsSchema.extend(geometrySearchRequestMandatory.shape);\n","import { bboxFromCoordsArray } from '@tomtom-org/maps-sdk/core';\nimport type { MultiPolygon, Position } from 'geojson';\nimport type { PostObject } from '../shared';\nimport { sampleWithinMaxLength } from '../shared/arrays';\nimport { positionToCSVLatLon } from '../shared/geometry';\nimport { appendCommonSearchParams, PLACES_URL_PATH } from '../shared/request/commonSearchRequestBuilder';\nimport type { GeometryAPI, GeometrySearchParams, GeometrySearchPayloadAPI, SearchGeometryInput } from './types';\n\nconst findFiftyLargestPolygons = (searchGeometry: MultiPolygon): Position[][][] => {\n // we calculate the size of each polygon based on bounding box (simplified)\n // we put it into a map for easy sorting\n // we sort the map by using size and keep only 50 largest polygons\n let polygonSizeMap = new Map<Position[][], number>();\n searchGeometry.coordinates.forEach((polygon) => {\n const bboxOfPolygon = bboxFromCoordsArray(polygon[0]);\n if (bboxOfPolygon) {\n const polygonSize = Math.abs((bboxOfPolygon[2] - bboxOfPolygon[0]) * (bboxOfPolygon[3] - bboxOfPolygon[1]));\n polygonSizeMap.set(polygon, polygonSize);\n }\n });\n polygonSizeMap = new Map([...polygonSizeMap.entries()].sort((a, b) => b[1] - a[1]).splice(0, 50));\n return [...polygonSizeMap.keys()];\n};\n\nconst sdkGeometryToApiGeometries = (searchGeometry: SearchGeometryInput): GeometryAPI[] => {\n switch (searchGeometry.type) {\n case 'Circle':\n return [\n {\n type: 'CIRCLE',\n radius: searchGeometry.radius,\n position: positionToCSVLatLon(searchGeometry.coordinates),\n },\n ];\n case 'Polygon':\n return [\n {\n type: 'POLYGON',\n vertices: sampleWithinMaxLength(searchGeometry.coordinates[0], 50).map((coord) =>\n positionToCSVLatLon(coord),\n ),\n },\n ];\n case 'MultiPolygon': {\n if (searchGeometry.coordinates.length > 50) {\n // we have too many polygons for the service to work\n return findFiftyLargestPolygons(searchGeometry).flatMap((polygonCoords) =>\n sdkGeometryToApiGeometries({ type: 'Polygon', coordinates: polygonCoords }),\n );\n }\n return searchGeometry.coordinates.flatMap((polygonCoords) =>\n sdkGeometryToApiGeometries({ type: 'Polygon', coordinates: polygonCoords }),\n );\n }\n case 'FeatureCollection':\n return searchGeometry.features.flatMap((feature) => sdkGeometryToApiGeometries(feature.geometry));\n default:\n // @ts-ignore\n throw new Error(`Type ${(searchGeometry as unknown).type} is not supported`);\n }\n};\n\nconst buildUrlBasePath = (mergedOptions: GeometrySearchParams): string =>\n mergedOptions.customServiceBaseURL ??\n `${mergedOptions.commonBaseURL}${PLACES_URL_PATH}/geometrySearch/${mergedOptions.query ?? ''}.json`;\n\n/**\n * Default function for building a geometry search request from {@link GeometrySearchParams}\n * @param params The geometry search parameters, with global configuration already merged into them.\n */\nexport const buildGeometrySearchRequest = (params: GeometrySearchParams): PostObject<GeometrySearchPayloadAPI> => {\n const url = new URL(`${buildUrlBasePath(params)}`);\n appendCommonSearchParams(url, params);\n\n return {\n url,\n data: {\n geometryList: params.geometries.flatMap(sdkGeometryToApiGeometries),\n },\n };\n};\n","import { bboxFromGeoJSON, bboxOnlyIfWithArea } from '@tomtom-org/maps-sdk/core';\nimport { parseSearchAPIResult, parseSummaryAPI } from '../shared/searchResultParsing';\nimport type { GeometrySearchResponse, GeometrySearchResponseAPI } from './types';\n\n/**\n * Default function to parse a geometry search response.\n * @param apiResponse The API response.\n */\nexport const parseGeometrySearchResponse = (apiResponse: GeometrySearchResponseAPI): GeometrySearchResponse => {\n const features = apiResponse.results.map(parseSearchAPIResult);\n const bbox = bboxOnlyIfWithArea(bboxFromGeoJSON(features));\n return {\n type: 'FeatureCollection',\n properties: {\n ...parseSummaryAPI(apiResponse.summary),\n },\n features,\n ...(bbox && { bbox }),\n };\n};\n","import type { ServiceTemplate } from '../shared';\nimport { post } from '../shared/fetch';\nimport { geometrySearchRequestSchema } from './geometrySearchRequestSchema';\nimport { buildGeometrySearchRequest } from './requestBuilder';\nimport { parseGeometrySearchResponse } from './responseParser';\nimport type {\n GeometrySearchParams,\n GeometrySearchRequestAPI,\n GeometrySearchResponse,\n GeometrySearchResponseAPI,\n} from './types';\n\n/**\n * Geometry search service template type.\n */\nexport type GeometrySearchTemplate = ServiceTemplate<\n GeometrySearchParams,\n GeometrySearchRequestAPI,\n GeometrySearchResponseAPI,\n GeometrySearchResponse\n>;\n\n/**\n * Geometry search service template main implementation.\n */\nexport const geometrySearchTemplate: GeometrySearchTemplate = {\n requestValidation: { schema: geometrySearchRequestSchema },\n buildRequest: buildGeometrySearchRequest,\n sendRequest: post,\n parseResponse: parseGeometrySearchResponse,\n};\n","import { callService } from '../shared/serviceTemplate';\nimport type { GeometrySearchTemplate } from './geometrySearchTemplate';\nimport { geometrySearchTemplate } from './geometrySearchTemplate';\nimport type { GeometrySearchParams, GeometrySearchResponse } from './types';\n\n/**\n * Search for places within specific geographic boundaries.\n *\n * The Geometry Search service finds places that fall within one or more defined areas,\n * such as polygons, circles, or corridors. This is essential for location-based filtering\n * and spatial queries where you need results constrained to specific regions.\n *\n * @remarks\n * Key features:\n * - **Area-based search**: Find places within polygons, circles, or along routes\n * - **Multiple geometries**: Search across several areas simultaneously\n * - **Precise boundaries**: Only returns results within the specified areas\n * - **Combined with text**: Filter by query text within the geometric boundaries\n * - **Category filtering**: Narrow results by POI categories\n *\n * Common use cases:\n * - **Route-based search**: \"Find gas stations along my route\"\n * - **Area filtering**: \"Restaurants within this neighborhood polygon\"\n * - **Corridor search**: \"Hotels within 5km of the highway\"\n * - **Service areas**: \"Stores within our delivery zone\"\n * - **Geofencing**: Places within administrative or custom boundaries\n *\n * @param params Geometry search parameters including geometries and optional query\n * @param customTemplate Advanced customization for request/response handling\n *\n * @returns Promise resolving to places within the specified geometries\n *\n * @example\n * ```typescript\n * // Search within a polygon (neighborhood boundaries)\n * const inArea = await geometrySearch({\n * query: 'coffee shop',\n * geometries: [{\n * type: 'Polygon',\n * coordinates: [[\n * [4.88, 52.36],\n * [4.90, 52.36],\n * [4.90, 52.38],\n * [4.88, 52.38],\n * [4.88, 52.36]\n * ]]\n * }]\n * });\n *\n * // Find POIs along a route (pass the route LineString as geometry)\n * const alongRoute = await geometrySearch({\n * query: 'gas station',\n * geometries: [routeLineString], // LineString from calculateRoute result\n * limit: 10\n * });\n *\n * // Search multiple areas at once\n * const multiArea = await geometrySearch({\n * query: 'pharmacy',\n * geometries: [polygonA, polygonB, polygonC],\n * limit: 20\n * });\n *\n * // Category search within geometry\n * const restaurants = await geometrySearch({\n * poiCategories: ['ITALIAN_RESTAURANT'],\n * geometries: [cityBoundary]\n * });\n * ```\n *\n * @see [Geometry Search API Documentation](https://docs.tomtom.com/search-api/documentation/search-service/geometry-search)\n * @see [Places Quickstart Guide](https://docs.tomtom.com/maps-sdk-js/guides/services/places/quickstart)\n * @see [Search Guide](https://docs.tomtom.com/maps-sdk-js/guides/services/places/search)\n *\n * @ignore (exposed via 'search')\n */\nexport const geometrySearch = async (\n params: GeometrySearchParams,\n customTemplate?: Partial<GeometrySearchTemplate>,\n): Promise<GeometrySearchResponse> =>\n callService(params, { ...geometrySearchTemplate, ...customTemplate }, 'GeometrySearch');\n","import { geometrySearch } from './geometrySearch';\nimport type { GeometrySearchTemplate } from './geometrySearchTemplate';\nimport { geometrySearchTemplate } from './geometrySearchTemplate';\nimport { buildGeometrySearchRequest } from './requestBuilder';\nimport { parseGeometrySearchResponse } from './responseParser';\n\nconst customize: {\n geometrySearch: typeof geometrySearch;\n buildGeometrySearchRequest: typeof buildGeometrySearchRequest;\n parseGeometrySearchResponse: typeof parseGeometrySearchResponse;\n geometrySearchTemplate: GeometrySearchTemplate;\n} = {\n geometrySearch,\n buildGeometrySearchRequest,\n parseGeometrySearchResponse,\n geometrySearchTemplate,\n};\nexport default customize;\n","import { views } from '@tomtom-org/maps-sdk/core';\nimport { z } from 'zod';\n\nconst placeByIdRequestMandatory = z.object({\n entityId: z.string(),\n});\n\nconst placeByIdRequestOptional = z.object({\n mapcodes: z.array(z.string()).optional(),\n view: z.enum(views).optional(),\n openingHours: z.string().optional(),\n timeZone: z.string().optional(),\n relatedPois: z.string().optional(),\n});\n\n/**\n * @ignore\n */\nexport const placeByIdRequestSchema = placeByIdRequestMandatory.extend(placeByIdRequestOptional.shape);\n","import type { CommonServiceParams } from '../shared';\nimport { PLACES_URL_PATH } from '../shared/request/commonSearchRequestBuilder';\nimport {\n appendByJoiningParamValue,\n appendCommonParams,\n appendOptionalParam,\n} from '../shared/request/requestBuildingUtils';\nimport type { PlaceByIdParams } from './types';\n\nconst buildUrlBasePath = (params: CommonServiceParams): string =>\n params.customServiceBaseURL || `${params.commonBaseURL}${PLACES_URL_PATH}/place.json`;\n\n/**\n * Default method for building place by id request from {@link PlaceByIdParams}\n * @param params The place by id parameters, with global configuration already merged into them.\n */\nexport const buildPlaceByIdRequest = (params: PlaceByIdParams): URL => {\n const url = new URL(`${buildUrlBasePath(params)}`);\n const urlParams = url.searchParams;\n appendCommonParams(urlParams, params);\n appendOptionalParam(urlParams, 'entityId', params.entityId);\n appendByJoiningParamValue(urlParams, 'mapcodes', params.mapcodes);\n appendOptionalParam(urlParams, 'view', params.view);\n appendOptionalParam(urlParams, 'openingHours', params.openingHours);\n appendOptionalParam(urlParams, 'timeZone', params.timeZone);\n appendOptionalParam(urlParams, 'relatedPois', params.relatedPois);\n return url;\n};\n","import { parseSearchAPIResult } from '../shared/searchResultParsing';\nimport type { PlaceByIdResponse, PlaceByIdResponseAPI } from './types';\n\n/**\n * Default method for parsing place by id response.\n * @param apiResponse The place by id response.\n */\nexport const parsePlaceByIdResponse = (apiResponse: PlaceByIdResponseAPI): PlaceByIdResponse =>\n apiResponse.results?.length ? parseSearchAPIResult(apiResponse.results[0]) : undefined;\n","import type { ServiceTemplate } from '../shared';\nimport { get } from '../shared/fetch';\nimport { placeByIdRequestSchema } from './placeByIdSchema';\nimport { buildPlaceByIdRequest } from './requestBuilder';\nimport { parsePlaceByIdResponse } from './responseParser';\nimport type { PlaceByIdParams, PlaceByIdResponse, PlaceByIdResponseAPI } from './types';\n\n/**\n * Place By Is template type.\n * @ignore\n */\nexport type PlaceByIdTemplate = ServiceTemplate<PlaceByIdParams, URL, PlaceByIdResponseAPI, PlaceByIdResponse>;\n\n/**\n * Place By Id template main implementation.\n * @ignore\n */\nexport const placeByIdTemplate: PlaceByIdTemplate = {\n requestValidation: { schema: placeByIdRequestSchema },\n buildRequest: buildPlaceByIdRequest,\n sendRequest: get,\n parseResponse: parsePlaceByIdResponse,\n};\n","import type { PlaceByIdTemplate } from './placeByIdTemplate';\nimport { placeByIdTemplate } from './placeByIdTemplate';\nimport { buildPlaceByIdRequest } from './requestBuilder';\nimport { parsePlaceByIdResponse } from './responseParser';\n\nconst customize: {\n buildPlaceByIdRequest: typeof buildPlaceByIdRequest;\n parsePlaceByIdResponse: typeof parsePlaceByIdResponse;\n placeByIdTemplate: PlaceByIdTemplate;\n} = {\n buildPlaceByIdRequest,\n parsePlaceByIdResponse,\n placeByIdTemplate,\n};\nexport default customize;\n","import type { ParseResponseError } from '../shared';\nimport { SDKServiceError } from '../shared';\nimport type { RoutingAPIResponseError } from '../shared/types/apiResponseErrorTypes';\n\n/**\n * @ignore\n * @param apiError\n * @param serviceName\n */\nexport const parseRoutingResponseError: ParseResponseError<RoutingAPIResponseError> = (apiError, serviceName) => {\n const { data, message, status } = apiError;\n const errorMessage = data?.error?.description ?? data?.detailedError?.message ?? message;\n return new SDKServiceError(errorMessage, serviceName, status);\n};\n","import { currentTypes, plugTypes } from '@tomtom-org/maps-sdk/core';\nimport { z } from 'zod';\n\n// Common validation schemas\nconst positiveNumber = z.number().positive();\nconst nonNegativeNumber = z.number().min(0);\nconst percentageNumber = z.number().min(0).max(100);\n\n// Common optional schemas\nconst optionalPositiveNumber = positiveNumber.optional();\nconst optionalNonNegativeNumber = nonNegativeNumber.optional();\nconst optionalNormalizedNumber = z.number().min(0).max(1).optional();\n\n// Speed to consumption rate schema\nconst speedToConsumptionRateSchema = z.object({\n speedKMH: z.number(),\n consumptionUnitsPer100KM: z.number(),\n});\n\n// Efficiency schema for consumption models\nconst efficiencySchema = z\n .object({\n acceleration: optionalNormalizedNumber,\n deceleration: optionalNormalizedNumber,\n uphill: optionalNormalizedNumber,\n downhill: optionalNormalizedNumber,\n })\n .optional();\n\n// Base consumption model schema\nconst baseConsumptionModelSchema = {\n efficiency: efficiencySchema,\n};\n\n// Consumption array validation\nconst speedConsumptionArray = z.array(speedToConsumptionRateSchema).min(1).max(25);\n\n// Combustion consumption model schema\nconst combustionConsumptionModelSchema = z.object({\n ...baseConsumptionModelSchema,\n speedsToConsumptionsLiters: speedConsumptionArray,\n auxiliaryPowerInLitersPerHour: optionalNonNegativeNumber,\n fuelEnergyDensityInMJoulesPerLiter: z.number().min(1).optional(),\n});\n\n// Electric consumption model schema\nconst electricConsumptionModelSchema = z.object({\n ...baseConsumptionModelSchema,\n speedsToConsumptionsKWH: speedConsumptionArray,\n auxiliaryPowerInkW: optionalNonNegativeNumber,\n consumptionInKWHPerKMAltitudeGain: z.number().max(500).optional(),\n recuperationInKWHPerKMAltitudeLoss: optionalNonNegativeNumber,\n});\n\n// Charging connector schema\nconst chargingConnectorSchema = z.object({\n currentType: z.enum(currentTypes),\n plugTypes: z.array(z.enum(plugTypes)).min(1),\n efficiency: optionalNormalizedNumber,\n baseLoadInkW: optionalNonNegativeNumber,\n maxPowerInkW: optionalNonNegativeNumber,\n maxVoltageInV: optionalNonNegativeNumber,\n maxCurrentInA: optionalNonNegativeNumber,\n voltageRange: z\n .object({\n minVoltageInV: optionalNonNegativeNumber,\n maxVoltageInV: z.number().optional(),\n })\n .optional(),\n});\n\n// Battery curve schema\nconst batteryCurveSchema = z.object({\n stateOfChargeInkWh: nonNegativeNumber,\n maxPowerInkW: positiveNumber,\n});\n\n// Charging model schema\nconst chargingModelSchema = z.object({\n maxChargeKWH: positiveNumber,\n batteryCurve: z.array(batteryCurveSchema).max(20).optional(),\n chargingConnectors: z.array(chargingConnectorSchema).min(1).optional(),\n chargingTimeOffsetInSec: optionalNonNegativeNumber,\n});\n\n// Engine model schemas\nconst combustionEngineModelSchema = z.object({\n consumption: combustionConsumptionModelSchema,\n});\nconst electricEngineModelSchema = z.object({\n consumption: electricConsumptionModelSchema,\n charging: chargingModelSchema.optional(),\n});\n\n// Vehicle dimensions schema\nconst vehicleDimensionsSchema = z\n .object({\n weightKG: optionalPositiveNumber,\n })\n .optional();\n\n// Vehicle model schemas\nconst predefinedVehicleModelSchema = z.object({\n variantId: z.string(),\n});\nconst explicitVehicleModelSchema = z.object({\n dimensions: vehicleDimensionsSchema,\n engine: z.union([combustionEngineModelSchema, electricEngineModelSchema]).optional(),\n});\nconst vehicleModelSchema = z.union([predefinedVehicleModelSchema, explicitVehicleModelSchema]).optional();\n\n// Vehicle state schemas\nconst genericVehicleStateSchema = z.object({\n heading: z.number().min(0).max(360).optional(),\n});\n\nconst combustionVehicleStateSchema = genericVehicleStateSchema.extend({\n currentFuelInLiters: nonNegativeNumber,\n});\n\nconst electricVehicleStateByPercentageSchema = genericVehicleStateSchema.extend({\n currentChargePCT: percentageNumber,\n});\n\nconst electricVehicleStateByKwhSchema = genericVehicleStateSchema.extend({\n currentChargeInkWh: nonNegativeNumber,\n});\n\nconst electricVehicleStateSchema = z.union([electricVehicleStateByPercentageSchema, electricVehicleStateByKwhSchema]);\n\n// Charging preferences schemas\nconst chargingPreferencesPCTSchema = z.object({\n minChargeAtDestinationPCT: percentageNumber,\n minChargeAtChargingStopsPCT: z.number().min(0).max(50),\n});\n\nconst chargingPreferencesKWHSchema = z.object({\n minChargeAtDestinationInkWh: nonNegativeNumber,\n minChargeAtChargingStopsInkWh: nonNegativeNumber,\n});\n\nconst chargingPreferencesSchema = z.union([chargingPreferencesPCTSchema, chargingPreferencesKWHSchema]);\nconst electricVehiclePreferencesSchema = z.object({\n chargingPreferences: chargingPreferencesSchema.optional(),\n});\n\n// Vehicle restrictions schema\nconst vehicleRestrictionsSchema = z\n .object({\n maxSpeedKMH: z.number().min(0).max(250).optional(),\n })\n .optional();\n\n// Base vehicle parameters schema\nconst baseVehicleParamsSchema = {\n model: vehicleModelSchema,\n restrictions: vehicleRestrictionsSchema,\n};\n\n// Vehicle parameters schemas\nconst genericVehicleParamsSchema = z.object({\n ...baseVehicleParamsSchema,\n engineType: z.undefined().optional(),\n state: genericVehicleStateSchema.optional(),\n preferences: z.object({}).optional(),\n});\n\nconst combustionVehicleParamsSchema = z.object({\n ...baseVehicleParamsSchema,\n engineType: z.literal('combustion'),\n state: combustionVehicleStateSchema.optional(),\n preferences: z.object({}).optional(),\n});\n\nconst electricVehicleParamsSchema = z.object({\n ...baseVehicleParamsSchema,\n engineType: z.literal('electric'),\n state: electricVehicleStateSchema.optional(),\n preferences: electricVehiclePreferencesSchema.optional(),\n});\n\n/**\n * @ignore\n */\nexport const vehicleParametersSchema = z.union([\n z.discriminatedUnion('engineType', [combustionVehicleParamsSchema, electricVehicleParamsSchema]),\n genericVehicleParamsSchema,\n]);\n","import { avoidableTypes } from '@tomtom-org/maps-sdk/core';\nimport { z } from 'zod';\nimport { routeTypes } from '../types/commonRoutingParams';\nimport { hasBBoxSchema } from './geometriesSchema';\nimport { vehicleParametersSchema } from './vehicleParamsSchema';\n\n/**\n * @ignore\n */\nexport const commonRoutingRequestSchema = z.object({\n costModel: z\n .object({\n avoid: z.array(z.enum(avoidableTypes)).optional(),\n traffic: z.enum(['live', 'historical']).optional(),\n routeType: z.enum(routeTypes).optional(),\n avoidAreas: z.array(hasBBoxSchema).max(10).optional(),\n })\n .optional(),\n travelMode: z.string().optional(),\n vehicle: vehicleParametersSchema.optional(),\n when: z\n .object({\n option: z.enum(['departAt', 'arriveBy']),\n date: z.date(),\n })\n .optional(),\n});\n","import type { Avoidable, HasLngLat } from '@tomtom-org/maps-sdk/core';\nimport type {\n CombustionVehicleParams,\n CombustionVehicleState,\n CommonRoutingParams,\n CommonServiceParams,\n CostModel,\n DepartArriveParams,\n ElectricVehicleParams,\n ElectricVehicleState,\n GenericVehicleParams,\n VehicleParameters,\n} from '../../shared';\nimport type { VehicleRestrictions } from '../../shared/types/vehicleRestrictionParams';\nimport type { ReachableRangeRequestAPI } from './apiRequestTypes';\nimport type { ReachableRangeResponseAPI } from './apiResponseTypes';\n\nexport const budgetTypes = [\n 'timeMinutes',\n 'remainingChargeCPT',\n 'spentChargePCT',\n 'spentFuelLiters',\n 'distanceKM',\n] as const;\n\n/**\n * Type of budget constraint for reachable range calculation.\n *\n * @remarks\n * **Budget Types:**\n * - `timeMinutes`: Travel time in minutes (isochrone)\n * - `distanceKM`: Travel distance in kilometers (isodistance)\n * - `remainingChargeCPT`: Remaining battery charge in percentage (EV)\n * - `spentChargePCT`: Battery charge consumed in percentage (EV)\n * - `spentFuelLiters`: Fuel consumed in liters (combustion)\n *\n * **Common Use Cases:**\n * - Time: \"Where can I reach in 30 minutes?\"\n * - Distance: \"What's within 10 km?\"\n * - EV charge: \"How far can I go on 50% battery?\"\n * - Fuel: \"Range with 20 liters of fuel\"\n *\n * @example\n * ```typescript\n * const timeType: BudgetType = 'timeMinutes';\n * const distanceType: BudgetType = 'distanceKM';\n * const evType: BudgetType = 'remainingChargeCPT';\n * ```\n *\n * @group Reachable Range\n */\nexport type BudgetType = (typeof budgetTypes)[number];\n\n/**\n * Budget constraint for reachable range calculation.\n *\n * Defines the limit (time, distance, or fuel/charge) for calculating\n * how far you can travel from a starting point.\n *\n * @remarks\n * **Use Cases:**\n * - Service area visualization (30-min delivery zone)\n * - EV range anxiety mitigation (show reachable area)\n * - Emergency response coverage (10-min response time)\n * - Delivery zone planning\n * - Store location analysis\n *\n * @example\n * ```typescript\n * // 30-minute travel time\n * const timeBudget: ReachableRangeBudget = {\n * type: 'timeMinutes',\n * value: 30\n * };\n *\n * // 50 km distance\n * const distanceBudget: ReachableRangeBudget = {\n * type: 'distanceKM',\n * value: 50\n * };\n *\n * // 50% battery remaining\n * const evBudget: ReachableRangeBudget = {\n * type: 'remainingChargeCPT',\n * value: 50\n * };\n *\n * // 20 liters of fuel\n * const fuelBudget: ReachableRangeBudget = {\n * type: 'spentFuelLiters',\n * value: 20\n * };\n * ```\n *\n * @group Reachable Range\n */\nexport type ReachableRangeBudget = {\n /**\n * The type of budget, including units.\n *\n * @remarks\n * - `timeMinutes`: Minutes of travel time\n * - `distanceKM`: Kilometers of travel distance\n * - `remainingChargeCPT`: Battery percentage remaining\n * - `spentChargePCT`: Battery percentage consumed\n * - `spentFuelLiters`: Liters of fuel consumed\n */\n type: BudgetType;\n\n /**\n * The value of the budget based on the units mentioned in the type.\n *\n * @remarks\n * **Typical Values:**\n * - Time: 5-60 minutes\n * - Distance: 5-100 km\n * - Charge: 10-100 percentage\n * - Fuel: 5-50 liters\n *\n * @example\n * ```typescript\n * value: 30 // 30 minutes/km/percent/liters depending on type\n * value: 15.5 // Decimal values supported\n * ```\n */\n value: number;\n};\n\n/**\n * Route features `calculateReachableRange` can avoid.\n *\n * @remarks\n * Every {@link Avoidable} except `alreadyUsedRoads`, which this endpoint rejects with\n * *\"Avoid alreadyUsedRoads not supported for calculateReachableRange\"*. It stays available on\n * `calculateRoute`.\n *\n * @group Reachable Range\n */\nexport type ReachableRangeAvoidable = Exclude<Avoidable, 'alreadyUsedRoads'>;\n\n/**\n * Cost model accepted by `calculateReachableRange`.\n *\n * @remarks\n * {@link CostModel} without `avoidAreas`, which this endpoint has no parameter for, and with the\n * narrower {@link ReachableRangeAvoidable} avoid list.\n *\n * @group Reachable Range\n */\nexport type ReachableRangeCostModel = Omit<CostModel, 'avoid' | 'avoidAreas'> & {\n /**\n * Route features to avoid when calculating the range.\n */\n avoid?: ReachableRangeAvoidable[];\n};\n\n/**\n * Vehicle parameters accepted by `calculateReachableRange`.\n *\n * @remarks\n * {@link VehicleParameters} without `state.heading`, which this endpoint rejects with\n * *\"parameter [vehicleHeading] not supported\"*. It stays available on `calculateRoute`, where the\n * routing API takes it as `vehicleHeadingInDegrees`.\n *\n * Everything else is unchanged, including the engine-specific `state` variants -- an electric\n * vehicle still picks between the percentage and kWh forms.\n *\n * @group Reachable Range\n */\nexport type ReachableRangeVehicleParameters = (\n | Omit<GenericVehicleParams, 'state'>\n | (Omit<CombustionVehicleParams, 'state'> & { state?: CombustionVehicleState })\n | (Omit<ElectricVehicleParams, 'state'> & { state?: ElectricVehicleState })\n) &\n VehicleRestrictions;\n\n/**\n * Parameters specific to reachable range calculation.\n *\n * @remarks\n * These parameters are combined with common routing parameters to calculate\n * the reachable area from a starting point.\n *\n * @group Reachable Range\n */\nexport type ReachableRangeOwnParams = {\n /**\n * How the routes underlying the range are optimized, and what they avoid.\n *\n * @remarks\n * Narrower than {@link CostModel}: `calculateReachableRange` rejects `alreadyUsedRoads` and has\n * no avoid-areas parameter, so neither is offered here.\n */\n costModel?: ReachableRangeCostModel;\n\n /**\n * Parameters for the vehicle whose reachable range is calculated.\n *\n * @remarks\n * Narrower than {@link VehicleParameters}: `calculateReachableRange` has no vehicle-heading\n * parameter, so {@link ReachableRangeVehicleParameters} does not offer `state.heading`.\n */\n vehicle?: ReachableRangeVehicleParameters;\n\n /**\n * Location from which the range calculation should start.\n *\n * @remarks\n * The center point from which reachability is calculated. Can be:\n * - An object with `lon` and `lat` properties\n * - An array `[longitude, latitude]`\n *\n * @example\n * ```typescript\n * // Object format\n * origin: { lon: 4.9, lat: 52.3 }\n *\n * // Array format\n * origin: [4.9, 52.3]\n * ```\n */\n origin: HasLngLat;\n\n /**\n * The budget for the reachable range calculation.\n *\n * @remarks\n * Consists of a type indicating whether it's about time, distance, or\n * fuel/charge and the units, plus its value.\n *\n * Determines the extent of the reachable area polygon.\n */\n budget: ReachableRangeBudget;\n\n /**\n * Maximum ferry length in meters to consider.\n *\n * @remarks\n * Ferries longer than this value will be avoided in the calculation.\n * Useful for excluding long ferry routes that might extend the range\n * unrealistically.\n *\n * @example\n * ```typescript\n * maxFerryLengthMeters: 5000 // Avoid ferries longer than 5 km\n * ```\n */\n maxFerryLengthMeters?: number;\n\n /**\n * Applies post-processing to smooth the polygon boundary for better visual appearance.\n *\n * @remarks\n * Stronger smoothing generally results in more points.\n *\n * @default `'strong'`\n *\n * @example\n * ```typescript\n * smoothing: 'strong'\n * ```\n */\n smoothing?: 'none' | 'weak' | 'strong';\n\n /**\n * Specifies when to depart.\n *\n * @remarks\n * If past dates are supplied or dates that are impossible to achieve,\n * it will default to departing now.\n *\n * **Traffic Impact:**\n * - Future departure times use predictive traffic\n * - Current time uses live traffic\n * - Historic times use historical patterns\n *\n * @default Depart now\n *\n * @example\n * ```typescript\n * when: { departAt: new Date('2025-10-20T08:00:00Z') }\n * when: { departAt: 'now' }\n * ```\n */\n when?: DepartArriveParams<'departAt'>;\n};\n\n/**\n * Complete parameters for calculating a reachable range.\n *\n * Combines common service parameters, routing parameters, and reachable range\n * specific options to compute an isochrone or isodistance polygon.\n *\n * @remarks\n * **What it Returns:**\n * A polygon representing the area reachable from the origin within the\n * specified budget (time, distance, or fuel/charge).\n *\n * **Use Cases:**\n * - Delivery zone visualization\n * - Service area mapping\n * - Emergency response coverage\n * - EV range display\n * - Store catchment areas\n * - Real estate search (30-min commute)\n *\n * **Traffic Consideration:**\n * Results vary based on departure time and traffic conditions. Use\n * appropriate `when` values for accurate predictions.\n *\n * @example\n * ```typescript\n * // 30-minute driving range\n * const params: ReachableRangeParams = {\n * key: 'your-api-key',\n * origin: [4.9, 52.3],\n * budget: {\n * type: 'timeMinutes',\n * value: 30\n * },\n * routeType: 'fastest',\n * traffic: 'live',\n * when: { departAt: 'now' }\n * };\n *\n * // 50 km distance range\n * const distanceParams: ReachableRangeParams = {\n * key: 'your-api-key',\n * origin: [4.9, 52.3],\n * budget: {\n * type: 'distanceKM',\n * value: 50\n * }\n * };\n *\n * // EV range with 50% battery\n * const evParams: ReachableRangeParams = {\n * key: 'your-api-key',\n * origin: [4.9, 52.3],\n * budget: {\n * type: 'remainingChargeCPT',\n * value: 50\n * },\n * vehicle: {\n * engineType: 'electric',\n * model: {\n * engine: {\n * consumption: {\n * charging: { maxChargeKWH: 100 }\n * }\n * }\n * },\n * state: {\n * currentChargePCT: 80\n * }\n * }\n * };\n *\n * // Avoid toll roads\n * const noTollParams: ReachableRangeParams = {\n * key: 'your-api-key',\n * origin: [4.9, 52.3],\n * budget: {\n * type: 'timeMinutes',\n * value: 45\n * },\n * avoid: ['tollRoads']\n * };\n * ```\n *\n * @group Reachable Range\n */\nexport type ReachableRangeParams = CommonServiceParams<ReachableRangeRequestAPI, ReachableRangeResponseAPI> &\n Omit<CommonRoutingParams, 'costModel' | 'vehicle'> &\n ReachableRangeOwnParams;\n","import { avoidableTypes } from '@tomtom-org/maps-sdk/core';\nimport { z } from 'zod';\nimport { commonRoutingRequestSchema } from '../shared/schema/commonRoutingRequestSchema';\nimport { hasLngLatSchema } from '../shared/schema/geometriesSchema';\nimport { vehicleParametersSchema } from '../shared/schema/vehicleParamsSchema';\nimport { routeTypes } from '../shared/types/commonRoutingParams';\nimport { budgetTypes } from './types/reachableRangeParams';\n\n// import { SchemaRefinement } from \"../shared/types/validation\";\n\nconst reachableRangeRequestSchemaMandatory = z.object({\n origin: hasLngLatSchema,\n budget: z.object({\n type: z.enum(budgetTypes),\n value: z.number().min(0),\n }),\n});\n\n// Narrower than `commonRoutingRequestSchema.costModel`: the endpoint rejects\n// `avoid=alreadyUsedRoads` outright, and has no avoid-areas parameter at all.\nconst reachableRangeAvoidableSchema = z.enum(avoidableTypes).exclude(['alreadyUsedRoads']);\n\n// Matches `ReachableRangeVehicleParameters`: the endpoint answers\n// `400 parameter [vehicleHeading] not supported`, so a heading fails validation here rather than\n// being quietly dropped for callers who are not type-checked.\nconst reachableRangeVehicleSchema = vehicleParametersSchema.check(\n z.refine(\n (vehicle) => !(vehicle.state && 'heading' in vehicle.state),\n 'vehicle.state.heading: calculateReachableRange has no vehicle heading parameter',\n ),\n);\n\nconst reachableRangeRequestSchemaOptional = z.object({\n vehicle: reachableRangeVehicleSchema.optional(),\n costModel: z\n .object({\n avoid: z.array(reachableRangeAvoidableSchema).optional(),\n traffic: z.enum(['live', 'historical']).optional(),\n routeType: z.enum(routeTypes).optional(),\n })\n .optional(),\n maxFerryLengthMeters: z.number().min(0).optional(),\n smoothing: z.enum(['none', 'weak', 'strong']).optional(),\n});\n\nconst reachableRangeRequestSchema = commonRoutingRequestSchema.extend(\n reachableRangeRequestSchemaMandatory.extend(reachableRangeRequestSchemaOptional.shape).shape,\n);\n\n// const departArriveRefinement: SchemaRefinement<ReachableRangeParams> = {\n// check: (data: ReachableRangeParams): boolean => (data.when?.option as string) != \"arriveBy\",\n// message: \"When calculating a reachable range, departure date-time can be specified, but not arrival date-time\"\n// };\n\n// const evRangeRefinement: SchemaRefinement<ReachableRangeParams> = {\n// check: (data: ReachableRangeParams): boolean =>\n// !(\n// (data.budget.type === \"remainingChargeCPT\" || data.budget.type === \"spentChargePCT\") &&\n// data.vehicle?.engine?.type != \"electric\"\n// ),\n// message: \"With an EV reachable range, the vehicle parameters must be set, with 'electric' engine type\"\n// };\n//\n// const fuelRangeRefinement: SchemaRefinement<ReachableRangeParams> = {\n// check: (data: ReachableRangeParams): boolean =>\n// !(data.budget.type === \"spentFuelLiters\" && data.vehicle?.engine?.type != \"combustion\"),\n// message: \"With a fuel reachable range, the vehicle parameters must be set, with 'combustion' engine type\"\n// };\n\n/**\n * @ignore\n */\nexport const reachableRangeRequestValidationConfig = {\n schema: reachableRangeRequestSchema,\n // refinements: [evRangeRefinement, fuelRangeRefinement, departArriveRefinement]\n};\n","import { isNil } from 'lodash-es';\nimport {\n CombustionEngineModel,\n ConsumptionModelEfficiency,\n ElectricConsumptionModel,\n ElectricEngineModel,\n SpeedToConsumptionRate,\n VehicleEngineModel,\n VehicleEngineType,\n} from '../types/vehicleEngineParams';\nimport { VehicleDimensions } from '../types/vehicleModel';\nimport { ElectricVehicleParams, VehicleParameters } from '../types/vehicleParams';\nimport { ChargingPreferencesKWH, ChargingPreferencesPCT } from '../types/vehiclePreferences';\nimport { ElectricVehicleStateKWH, ElectricVehicleStatePCT, VehicleState } from '../types/vehicleState';\n\nconst appendConsumptionEfficiency = (urlParams: URLSearchParams, efficiency?: ConsumptionModelEfficiency): void => {\n if (efficiency) {\n !isNil(efficiency.acceleration) && urlParams.append('accelerationEfficiency', String(efficiency.acceleration));\n !isNil(efficiency.deceleration) && urlParams.append('decelerationEfficiency', String(efficiency.deceleration));\n !isNil(efficiency.uphill) && urlParams.append('uphillEfficiency', String(efficiency.uphill));\n !isNil(efficiency.downhill) && urlParams.append('downhillEfficiency', String(efficiency.downhill));\n }\n};\n\n// e.g. 50,6.3:130,11.5\nconst buildSpeedToConsumptionString = (speedsToConsumptions: SpeedToConsumptionRate[]): string =>\n speedsToConsumptions\n .map((speedToConsumption) => `${speedToConsumption.speedKMH},${speedToConsumption.consumptionUnitsPer100KM}`)\n .join(':');\n\nconst appendCombustionEngine = (urlParams: URLSearchParams, engine: CombustionEngineModel): void => {\n // (no need to append combustion vehicleEngineType since it's the default)\n const consumptionModel = engine.consumption;\n consumptionModel.speedsToConsumptionsLiters &&\n urlParams.append(\n 'constantSpeedConsumptionInLitersPerHundredkm',\n buildSpeedToConsumptionString(consumptionModel.speedsToConsumptionsLiters),\n );\n !isNil(consumptionModel.auxiliaryPowerInLitersPerHour) &&\n urlParams.append('auxiliaryPowerInLitersPerHour', String(consumptionModel.auxiliaryPowerInLitersPerHour));\n !isNil(consumptionModel.fuelEnergyDensityInMJoulesPerLiter) &&\n urlParams.append(\n 'fuelEnergyDensityInMJoulesPerLiter',\n String(consumptionModel.fuelEnergyDensityInMJoulesPerLiter),\n );\n};\n\nconst appendElectricConsumptionModel = (urlParams: URLSearchParams, model: ElectricConsumptionModel): void => {\n model.speedsToConsumptionsKWH &&\n urlParams.append(\n 'constantSpeedConsumptionInkWhPerHundredkm',\n buildSpeedToConsumptionString(model.speedsToConsumptionsKWH),\n );\n !isNil(model.auxiliaryPowerInkW) && urlParams.append('auxiliaryPowerInkW', String(model.auxiliaryPowerInkW));\n !isNil(model.consumptionInKWHPerKMAltitudeGain) &&\n urlParams.append('consumptionInkWhPerkmAltitudeGain', String(model.consumptionInKWHPerKMAltitudeGain));\n !isNil(model.recuperationInKWHPerKMAltitudeLoss) &&\n urlParams.append('recuperationInkWhPerkmAltitudeLoss', String(model.recuperationInKWHPerKMAltitudeLoss));\n};\n\nconst appendChargingModel = (urlParams: URLSearchParams, engine: ElectricEngineModel): void => {\n const chargingModel = engine.charging;\n if (chargingModel?.maxChargeKWH) {\n urlParams.append('maxChargeInkWh', String(chargingModel.maxChargeKWH));\n }\n // (the rest of the charging model goes as POST data)\n};\n\nconst appendVehicleState = (urlParams: URLSearchParams, vehicleParams: VehicleParameters): void => {\n if (!vehicleParams.state) {\n return;\n }\n\n if (!('engineType' in vehicleParams)) {\n // Generic vehicle, no engine-specific state to append:\n return;\n }\n\n // Engine-specific state props:\n if (vehicleParams.engineType === 'combustion') {\n const combustionState: VehicleState<'combustion'> = vehicleParams.state;\n combustionState.currentFuelInLiters &&\n urlParams.append('currentFuelInLiters', String(combustionState.currentFuelInLiters));\n } else if (vehicleParams.engineType === 'electric') {\n const electricState: VehicleState<'electric'> = vehicleParams.state;\n const kwhElectricState = electricState as ElectricVehicleStateKWH;\n const pctElectricState = electricState as ElectricVehicleStatePCT;\n\n if (kwhElectricState.currentChargeInkWh) {\n urlParams.append('currentChargeInkWh', String(kwhElectricState.currentChargeInkWh));\n } else if (\n pctElectricState.currentChargePCT &&\n vehicleParams.model &&\n 'engine' in vehicleParams.model &&\n vehicleParams.model.engine\n ) {\n const engine = vehicleParams.model.engine;\n const maxChargeKWH = engine.charging?.maxChargeKWH;\n if (maxChargeKWH) {\n // currentChargePCT needs maxChargeKWH to be converted to kWh\n urlParams.append(\n 'currentChargeInkWh',\n String((maxChargeKWH * pctElectricState.currentChargePCT) / 100),\n );\n }\n }\n }\n};\n\nconst appendVehiclePreferences = (urlParams: URLSearchParams, vehicleParams: VehicleParameters): void => {\n if (!vehicleParams.preferences) {\n return;\n }\n\n if ('engineType' in vehicleParams && vehicleParams.engineType === 'electric') {\n const preferences = vehicleParams.preferences;\n if (preferences.chargingPreferences) {\n const chargingPrefs = preferences.chargingPreferences;\n const kwhChargingPrefs = chargingPrefs as ChargingPreferencesKWH;\n const pctChargingPrefs = chargingPrefs as ChargingPreferencesPCT;\n\n // Check if absolute kWh values are available and use them directly\n if (kwhChargingPrefs.minChargeAtChargingStopsInkWh || kwhChargingPrefs.minChargeAtDestinationInkWh) {\n urlParams.append('minChargeAtDestinationInkWh', String(kwhChargingPrefs.minChargeAtDestinationInkWh));\n urlParams.append(\n 'minChargeAtChargingStopsInkWh',\n String(kwhChargingPrefs.minChargeAtChargingStopsInkWh),\n );\n } else if (\n (pctChargingPrefs.minChargeAtChargingStopsPCT || pctChargingPrefs.minChargeAtDestinationPCT) &&\n vehicleParams.model &&\n 'engine' in vehicleParams.model &&\n vehicleParams.model.engine\n ) {\n // Considering percentage values if absolute values not available and maxChargeKWH exists\n const engine = vehicleParams.model.engine;\n const maxChargeKWH = engine.charging?.maxChargeKWH;\n if (maxChargeKWH) {\n urlParams.append(\n 'minChargeAtDestinationInkWh',\n String((maxChargeKWH * pctChargingPrefs.minChargeAtDestinationPCT) / 100),\n );\n urlParams.append(\n 'minChargeAtChargingStopsInkWh',\n String((maxChargeKWH * pctChargingPrefs.minChargeAtChargingStopsPCT) / 100),\n );\n }\n }\n }\n }\n};\n\nconst appendVehicleDimensions = (urlParams: URLSearchParams, dimensions?: VehicleDimensions): void => {\n // (default is 0):\n if (dimensions?.weightKG) urlParams.append('vehicleWeight', String(dimensions.weightKG));\n};\n\nconst appendVehicleRestrictions = (urlParams: URLSearchParams, vehicleParams: VehicleParameters): void => {\n // (default is 0):\n const maxSpeedKMH = vehicleParams.restrictions?.maxSpeedKMH;\n if (maxSpeedKMH) urlParams.append('vehicleMaxSpeed', String(maxSpeedKMH));\n};\n\nconst appendVehicleEngineModel = (\n urlParams: URLSearchParams,\n engineType: VehicleEngineType,\n engine: VehicleEngineModel<VehicleEngineType>,\n): void => {\n // (efficiency params have the same names between engine types)\n appendConsumptionEfficiency(urlParams, engine.consumption.efficiency);\n\n if (engineType === 'electric') {\n appendElectricConsumptionModel(urlParams, (engine as ElectricEngineModel).consumption);\n appendChargingModel(urlParams, engine as ElectricEngineModel);\n } else {\n // (no need to append combustion vehicleEngineType since it's the default)\n appendCombustionEngine(urlParams, engine as CombustionEngineModel);\n }\n};\n\nconst appendVehicleModel = (urlParams: URLSearchParams, vehicleParams: VehicleParameters): void => {\n if (!vehicleParams.model) {\n return;\n }\n\n // Handle predefined vehicle model\n if ('variantId' in vehicleParams.model) {\n urlParams.append('vehicleModelId', vehicleParams.model.variantId);\n } else {\n // Handle explicit vehicle model (dimensions and engine)\n appendVehicleDimensions(urlParams, vehicleParams.model.dimensions);\n\n if (vehicleParams.model.engine) {\n appendVehicleEngineModel(\n urlParams,\n 'engineType' in vehicleParams ? vehicleParams.engineType : undefined,\n vehicleParams.model.engine,\n );\n }\n }\n};\n\n/**\n * Appends vehicle parameters to the URL search params for routing requests.\n * @param urlParams - The URLSearchParams to append to\n * @param vehicleParams - The vehicle parameters to append\n */\nexport const appendVehicleParams = (urlParams: URLSearchParams, vehicleParams?: VehicleParameters): void => {\n if (!vehicleParams) {\n return;\n }\n\n // the engine type defaults to combustion, thus we only bother to append it if it's electric:\n if ((vehicleParams as ElectricVehicleParams).engineType === 'electric') {\n urlParams.append('vehicleEngineType', 'electric');\n }\n\n appendVehicleModel(urlParams, vehicleParams);\n appendVehicleState(urlParams, vehicleParams);\n appendVehiclePreferences(urlParams, vehicleParams);\n appendVehicleRestrictions(urlParams, vehicleParams);\n};\n","import type { CommonRoutingParams, DepartArriveParams } from '../types/commonRoutingParams';\nimport { appendByRepeatingParamName, appendOptionalParam } from './requestBuildingUtils';\nimport { appendVehicleParams } from './routingVehicleParamsBuilder';\n\nconst appendWhenParams = (urlParams: URLSearchParams, when?: DepartArriveParams): void => {\n if (when?.date) {\n const formattedDate = when.date.toISOString();\n if (when.option === 'departAt') {\n urlParams.append('departAt', formattedDate);\n } else if (when.option === 'arriveBy') {\n urlParams.append('arriveAt', formattedDate);\n }\n }\n};\n\n/**\n * @ignore\n */\nexport const appendCommonRoutingParams = (urlParams: URLSearchParams, params: CommonRoutingParams): void => {\n const costModel = params.costModel;\n appendByRepeatingParamName(urlParams, 'avoid', costModel?.avoid);\n appendOptionalParam(urlParams, 'traffic', costModel?.traffic);\n appendWhenParams(urlParams, params.when);\n appendOptionalParam(urlParams, 'routeType', costModel?.routeType);\n appendOptionalParam(urlParams, 'travelMode', params.travelMode);\n appendVehicleParams(urlParams, params.vehicle);\n};\n","import { getPositionStrict } from '@tomtom-org/maps-sdk/core';\nimport type { ElectricVehicleParams, ElectricVehicleStatePCT, FetchInput } from '../shared';\nimport { appendCommonRoutingParams } from '../shared/request/commonRoutingRequestBuilder';\nimport { appendCommonParams, appendOptionalParam } from '../shared/request/requestBuildingUtils';\nimport type { ReachableRangeParams } from './types/reachableRangeParams';\n\nconst buildUrlBasePath = (params: ReachableRangeParams, latLon: string): string =>\n params.customServiceBaseURL ?? `${params.commonBaseURL}/maps/orbis/routing/calculateReachableRange/${latLon}/json`;\n\nconst getMaxChargeKWH = (params: ReachableRangeParams): number | undefined => {\n const vehicle = params.vehicle as ElectricVehicleParams | undefined;\n if (!vehicle?.model || !('engine' in vehicle.model)) {\n return undefined;\n }\n return vehicle.model.engine?.charging?.maxChargeKWH;\n};\n\nconst getCurrentChargeKWH = (params: ReachableRangeParams): number | undefined => {\n const vehicle = params.vehicle as ElectricVehicleParams | undefined;\n if (!vehicle?.state) {\n return undefined;\n }\n const pctState = vehicle.state as ElectricVehicleStatePCT;\n if (pctState.currentChargePCT != null) {\n const maxChargeKWH = getMaxChargeKWH(params);\n if (maxChargeKWH != null) {\n return (maxChargeKWH * pctState.currentChargePCT) / 100;\n }\n }\n return undefined;\n};\n\nconst appendBudget = (urlParams: URLSearchParams, params: ReachableRangeParams): void => {\n const budget = params.budget;\n switch (budget.type) {\n case 'timeMinutes':\n urlParams.append('timeBudgetInSec', (budget.value * 60).toString());\n break;\n case 'distanceKM':\n urlParams.append('distanceBudgetInMeters', (budget.value * 1000).toString());\n break;\n case 'spentFuelLiters':\n urlParams.append('fuelBudgetInLiters', budget.value.toString());\n break;\n case 'spentChargePCT': {\n const maxChargeKWH = getMaxChargeKWH(params);\n if (maxChargeKWH != null) {\n urlParams.append('energyBudgetInkWh', ((maxChargeKWH * budget.value) / 100).toString());\n }\n break;\n }\n case 'remainingChargeCPT': {\n const currentChargeKWH = getCurrentChargeKWH(params);\n const maxChargeKWH = getMaxChargeKWH(params);\n if (maxChargeKWH != null && currentChargeKWH != null) {\n const remainingKWH = (maxChargeKWH * budget.value) / 100;\n urlParams.append('energyBudgetInkWh', Math.max(0, currentChargeKWH - remainingKWH).toString());\n }\n break;\n }\n default:\n // Unsupported by SDK but will attempt to send it anyway\n urlParams.append(budget.type, budget.value.toString());\n }\n};\n\nconst buildLatLon = (params: ReachableRangeParams): string => {\n const position = getPositionStrict(params.origin);\n return `${position[1]},${position[0]}`;\n};\n\n/**\n * @param params\n * @returns\n */\nexport const buildReachableRangeRequest = (params: ReachableRangeParams): FetchInput => {\n const latLon = buildLatLon(params);\n const url = new URL(buildUrlBasePath(params, latLon));\n const urlParams = url.searchParams;\n appendCommonParams(urlParams, params);\n // The reachable range API does not support language\n urlParams.delete('language');\n appendCommonRoutingParams(urlParams, params);\n appendBudget(urlParams, params);\n appendOptionalParam(urlParams, 'maxFerryLengthInMeters', params.maxFerryLengthMeters);\n appendOptionalParam(urlParams, 'smoothing', params.smoothing ?? 'strong');\n return { method: 'GET', url };\n};\n","import type { BBox, PolygonFeature } from '@tomtom-org/maps-sdk/core';\nimport { bboxFromGeoJSON } from '@tomtom-org/maps-sdk/core';\nimport type { Polygon } from 'geojson';\nimport type { ReachableRangeResponseAPI } from './types/apiResponseTypes';\nimport type { ReachableRangeParams } from './types/reachableRangeParams';\n\n/**\n *\n * @param apiResponse\n * @param params\n */\nexport const parseReachableRangeResponse = (\n apiResponse: ReachableRangeResponseAPI,\n params: ReachableRangeParams,\n): PolygonFeature<ReachableRangeParams> => {\n const geometry: Polygon = {\n type: 'Polygon',\n coordinates: [apiResponse.reachableRange.boundary.map((point) => [point.longitude, point.latitude])],\n };\n const bbox = bboxFromGeoJSON(geometry) as BBox; // the geometry should always have a bbox\n return { type: 'Feature', geometry, bbox, properties: params };\n};\n","import type { PolygonFeature } from '@tomtom-org/maps-sdk/core';\nimport { parseRoutingResponseError } from '../routing/routingResponseErrorParser';\nimport type { ServiceTemplate } from '../shared';\nimport { fetchWith } from '../shared/fetch';\nimport { reachableRangeRequestValidationConfig } from './reachableRangeRequestSchema';\nimport { buildReachableRangeRequest } from './requestBuilder';\nimport { parseReachableRangeResponse } from './responseParser';\nimport type { ReachableRangeRequestAPI } from './types/apiRequestTypes';\nimport type { ReachableRangeResponseAPI } from './types/apiResponseTypes';\nimport type { ReachableRangeParams } from './types/reachableRangeParams';\n\nexport type ReachableRangeTemplate = ServiceTemplate<\n ReachableRangeParams,\n ReachableRangeRequestAPI,\n ReachableRangeResponseAPI,\n PolygonFeature<ReachableRangeParams>\n>;\n\nexport const reachableRangeTemplate: ReachableRangeTemplate = {\n requestValidation: reachableRangeRequestValidationConfig,\n buildRequest: buildReachableRangeRequest,\n sendRequest: fetchWith,\n parseResponse: parseReachableRangeResponse,\n parseResponseError: parseRoutingResponseError,\n getAPIVersion: () => 2,\n};\n","import type { ReachableRangeTemplate } from './reachableRangeTemplate';\nimport { reachableRangeTemplate } from './reachableRangeTemplate';\nimport { buildReachableRangeRequest } from './requestBuilder';\nimport { parseReachableRangeResponse } from './responseParser';\n\nconst customize: {\n buildReachableRangeRequest: typeof buildReachableRangeRequest;\n parseReachableRangeResponse: typeof parseReachableRangeResponse;\n reachableRangeTemplate: ReachableRangeTemplate;\n} = {\n buildReachableRangeRequest,\n parseReachableRangeResponse,\n reachableRangeTemplate,\n};\nexport default customize;\n","import type { GeographyType } from '@tomtom-org/maps-sdk/core';\nimport { getPositionStrict } from '@tomtom-org/maps-sdk/core';\nimport { isNil } from 'lodash-es';\nimport type { GetObject } from '../shared';\nimport { PLACES_URL_PATH } from '../shared/request/commonSearchRequestBuilder';\nimport { buildCommonServiceRequestHeaders } from '../shared/request/requestBuildingUtils';\nimport type { ReverseGeocodingAreaTypeAPI } from './types/apiTypes';\nimport type { ReverseGeocodingParams } from './types/reverseGeocodingParams';\n\n// Maps the SDK's public GeographyType values to the API's `areaTypes` values.\nexport const AREA_TYPE_BY_GEOGRAPHY_TYPE: Record<GeographyType, ReverseGeocodingAreaTypeAPI> = {\n Country: 'country',\n CountrySubdivision: 'countrySubdivision',\n CountrySecondarySubdivision: 'countrySecondarySubdivision',\n CountryTertiarySubdivision: 'countryTertiarySubdivision',\n Municipality: 'municipality',\n MunicipalitySubdivision: 'municipalitySubdivision',\n MunicipalitySecondarySubdivision: 'municipalitySecondarySubdivision',\n Neighbourhood: 'neighborhood',\n PostalCodeArea: 'postalCode',\n};\n\n// Requests every result field the SDK's response parser understands, so callers get a\n// complete place without having to specify a projection themselves.\nconst DEFAULT_ATTRIBUTES = 'results(*)';\n\nconst buildUrlBasePath = (params: ReverseGeocodingParams): string =>\n params.customServiceBaseURL ?? `${params.commonBaseURL}${PLACES_URL_PATH}/reverseGeocode`;\n\n/**\n * Default function for building a reverse geocoding request from {@link ReverseGeocodingParams}\n * @param params The reverse geocoding parameters, with global configuration already merged into them.\n */\nexport const buildRevGeoRequest = (params: ReverseGeocodingParams): GetObject => {\n const [lng, lat] = getPositionStrict(params.position);\n const url = new URL(buildUrlBasePath(params));\n const urlParams = url.searchParams;\n urlParams.append('position', `${lng},${lat}`);\n\n !isNil(params.radiusMeters) && urlParams.append('radiusInMeters', String(params.radiusMeters));\n params.geographyType &&\n urlParams.append(\n 'areaTypes',\n params.geographyType.map((geographyType) => AREA_TYPE_BY_GEOGRAPHY_TYPE[geographyType]).join(','),\n );\n !isNil(params.heading) && urlParams.append('vehicleHeadingInDegrees', String(params.heading));\n params.view && urlParams.append('geopoliticalView', params.view);\n\n return {\n url,\n headers: {\n ...buildCommonServiceRequestHeaders(params),\n Attributes: DEFAULT_ATTRIBUTES,\n },\n };\n};\n","const ISO2_TO_ISO3: Record<string, string> = {\n AF: 'AFG',\n AX: 'ALA',\n AL: 'ALB',\n DZ: 'DZA',\n AS: 'ASM',\n AD: 'AND',\n AO: 'AGO',\n AI: 'AIA',\n AQ: 'ATA',\n AG: 'ATG',\n AR: 'ARG',\n AM: 'ARM',\n AW: 'ABW',\n AU: 'AUS',\n AT: 'AUT',\n AZ: 'AZE',\n BS: 'BHS',\n BH: 'BHR',\n BD: 'BGD',\n BB: 'BRB',\n BY: 'BLR',\n BE: 'BEL',\n BZ: 'BLZ',\n BJ: 'BEN',\n BM: 'BMU',\n BT: 'BTN',\n BO: 'BOL',\n BQ: 'BES',\n BA: 'BIH',\n BW: 'BWA',\n BV: 'BVT',\n BR: 'BRA',\n IO: 'IOT',\n BN: 'BRN',\n BG: 'BGR',\n BF: 'BFA',\n BI: 'BDI',\n CV: 'CPV',\n KH: 'KHM',\n CM: 'CMR',\n CA: 'CAN',\n KY: 'CYM',\n CF: 'CAF',\n TD: 'TCD',\n CL: 'CHL',\n CN: 'CHN',\n CX: 'CXR',\n CC: 'CCK',\n CO: 'COL',\n KM: 'COM',\n CG: 'COG',\n CD: 'COD',\n CK: 'COK',\n CR: 'CRI',\n CI: 'CIV',\n HR: 'HRV',\n CU: 'CUB',\n CW: 'CUW',\n CY: 'CYP',\n CZ: 'CZE',\n DK: 'DNK',\n DJ: 'DJI',\n DM: 'DMA',\n DO: 'DOM',\n EC: 'ECU',\n EG: 'EGY',\n SV: 'SLV',\n GQ: 'GNQ',\n ER: 'ERI',\n EE: 'EST',\n SZ: 'SWZ',\n ET: 'ETH',\n FK: 'FLK',\n FO: 'FRO',\n FJ: 'FJI',\n FI: 'FIN',\n FR: 'FRA',\n GF: 'GUF',\n PF: 'PYF',\n TF: 'ATF',\n GA: 'GAB',\n GM: 'GMB',\n GE: 'GEO',\n DE: 'DEU',\n GH: 'GHA',\n GI: 'GIB',\n GR: 'GRC',\n GL: 'GRL',\n GD: 'GRD',\n GP: 'GLP',\n GU: 'GUM',\n GT: 'GTM',\n GG: 'GGY',\n GN: 'GIN',\n GW: 'GNB',\n GY: 'GUY',\n HT: 'HTI',\n HM: 'HMD',\n VA: 'VAT',\n HN: 'HND',\n HK: 'HKG',\n HU: 'HUN',\n IS: 'ISL',\n IN: 'IND',\n ID: 'IDN',\n IR: 'IRN',\n IQ: 'IRQ',\n IE: 'IRL',\n IM: 'IMN',\n IL: 'ISR',\n IT: 'ITA',\n JM: 'JAM',\n JP: 'JPN',\n JE: 'JEY',\n JO: 'JOR',\n KZ: 'KAZ',\n KE: 'KEN',\n KI: 'KIR',\n KP: 'PRK',\n KR: 'KOR',\n KW: 'KWT',\n KG: 'KGZ',\n LA: 'LAO',\n LV: 'LVA',\n LB: 'LBN',\n LS: 'LSO',\n LR: 'LBR',\n LY: 'LBY',\n LI: 'LIE',\n LT: 'LTU',\n LU: 'LUX',\n MO: 'MAC',\n MG: 'MDG',\n MW: 'MWI',\n MY: 'MYS',\n MV: 'MDV',\n ML: 'MLI',\n MT: 'MLT',\n MH: 'MHL',\n MQ: 'MTQ',\n MR: 'MRT',\n MU: 'MUS',\n YT: 'MYT',\n MX: 'MEX',\n FM: 'FSM',\n MD: 'MDA',\n MC: 'MCO',\n MN: 'MNG',\n ME: 'MNE',\n MS: 'MSR',\n MA: 'MAR',\n MZ: 'MOZ',\n MM: 'MMR',\n NA: 'NAM',\n NR: 'NRU',\n NP: 'NPL',\n NL: 'NLD',\n NC: 'NCL',\n NZ: 'NZL',\n NI: 'NIC',\n NE: 'NER',\n NG: 'NGA',\n NU: 'NIU',\n NF: 'NFK',\n MK: 'MKD',\n MP: 'MNP',\n NO: 'NOR',\n OM: 'OMN',\n PK: 'PAK',\n PW: 'PLW',\n PS: 'PSE',\n PA: 'PAN',\n PG: 'PNG',\n PY: 'PRY',\n PE: 'PER',\n PH: 'PHL',\n PN: 'PCN',\n PL: 'POL',\n PT: 'PRT',\n PR: 'PRI',\n QA: 'QAT',\n RE: 'REU',\n RO: 'ROU',\n RU: 'RUS',\n RW: 'RWA',\n BL: 'BLM',\n SH: 'SHN',\n KN: 'KNA',\n LC: 'LCA',\n MF: 'MAF',\n PM: 'SPM',\n VC: 'VCT',\n WS: 'WSM',\n SM: 'SMR',\n ST: 'STP',\n SA: 'SAU',\n SN: 'SEN',\n RS: 'SRB',\n SC: 'SYC',\n SL: 'SLE',\n SG: 'SGP',\n SX: 'SXM',\n SK: 'SVK',\n SI: 'SVN',\n SB: 'SLB',\n SO: 'SOM',\n ZA: 'ZAF',\n GS: 'SGS',\n SS: 'SSD',\n ES: 'ESP',\n LK: 'LKA',\n SD: 'SDN',\n SR: 'SUR',\n SJ: 'SJM',\n SE: 'SWE',\n CH: 'CHE',\n SY: 'SYR',\n TW: 'TWN',\n TJ: 'TJK',\n TZ: 'TZA',\n TH: 'THA',\n TL: 'TLS',\n TG: 'TGO',\n TK: 'TKL',\n TO: 'TON',\n TT: 'TTO',\n TN: 'TUN',\n TR: 'TUR',\n TM: 'TKM',\n TC: 'TCA',\n TV: 'TUV',\n UG: 'UGA',\n UA: 'UKR',\n AE: 'ARE',\n GB: 'GBR',\n US: 'USA',\n UM: 'UMI',\n UY: 'URY',\n UZ: 'UZB',\n VU: 'VUT',\n VE: 'VEN',\n VN: 'VNM',\n VG: 'VGB',\n VI: 'VIR',\n WF: 'WLF',\n EH: 'ESH',\n YE: 'YEM',\n ZM: 'ZMB',\n ZW: 'ZWE',\n};\n\nexport const toIso3 = (iso2: string): string => ISO2_TO_ISO3[iso2] ?? iso2;\n","import type { GeographyType } from '@tomtom-org/maps-sdk/core';\nimport { generateId, getPositionStrict, toPointGeometry } from '@tomtom-org/maps-sdk/core';\nimport { toIso3 } from '../shared/iso2ToIso3';\nimport { AREA_TYPE_BY_GEOGRAPHY_TYPE } from './requestBuilder';\nimport type { ReverseGeocodingResponse } from './reverseGeocoding';\nimport type {\n ReverseGeocodingAddressAPI,\n ReverseGeocodingAreaTypeAPI,\n ReverseGeocodingResponseAPI,\n ReverseGeocodingResultTypeAPI,\n} from './types/apiTypes';\nimport type { ReverseGeocodingParams } from './types/reverseGeocodingParams';\n\nconst PLACE_TYPE_BY_RESULT_TYPE: Record<ReverseGeocodingResultTypeAPI, 'Street' | 'Geography' | 'Point Address'> = {\n street: 'Street',\n area: 'Geography',\n address: 'Point Address',\n};\n\nconst GEOGRAPHY_TYPE_BY_AREA_TYPE = Object.fromEntries(\n Object.entries(AREA_TYPE_BY_GEOGRAPHY_TYPE).map(([geographyType, areaType]) => [areaType, geographyType]),\n) as Record<ReverseGeocodingAreaTypeAPI, GeographyType>;\n\n// Renames the handful of v2 address fields whose SDK-facing name differs from the API's, and\n// derives `countryCodeISO3`, which v2 does not return but the v1 search services do. Same\n// derivation the routing response parser applies to its own `countryCodeIso2` fields.\nconst mapAddress = (apiAddress: ReverseGeocodingAddressAPI) => {\n const { street, houseNumber, countryCodeIso2, ...rest } = apiAddress;\n return {\n ...rest,\n ...(street && { streetName: street }),\n ...(houseNumber && { streetNumber: houseNumber }),\n ...(countryCodeIso2 && { countryCode: countryCodeIso2, countryCodeISO3: toIso3(countryCodeIso2) }),\n };\n};\n\n/**\n * Default method for parsing reverse geocoding request from {@link ReverseGeocodingResponse}\n * @param params\n * @param apiResponse\n */\nexport const parseRevGeoResponse = (\n apiResponse: ReverseGeocodingResponseAPI,\n params: ReverseGeocodingParams,\n): ReverseGeocodingResponse => {\n const firstResult = apiResponse.results[0];\n return {\n type: 'Feature',\n // The requested coordinates are the primary ones, and set as the GeoJSON Feature geometry:\n geometry: toPointGeometry(getPositionStrict(params.position)),\n id: firstResult?.id ?? generateId(),\n ...(firstResult && {\n properties: {\n type: PLACE_TYPE_BY_RESULT_TYPE[firstResult.type],\n address: { freeformAddress: firstResult.title, ...mapAddress(firstResult.address) },\n ...(firstResult.areaType && {\n geographyType: [GEOGRAPHY_TYPE_BY_AREA_TYPE[firstResult.areaType]],\n // v2 has no `dataSources`, but for an area result its `id` is the geometry ID.\n // Surfacing it here keeps `getGeometryData` / `GeometriesModule` reachable from a\n // reverse geocoded geography, as it is from a search result.\n dataSources: { geometry: { id: firstResult.id } },\n }),\n ...(firstResult.accessPoints?.length && {\n accessPoints: firstResult.accessPoints.map((accessPoint) => accessPoint.position.coordinates),\n }),\n // The reverse geocoded coordinates are secondary and set in the GeoJSON properties:\n originalPosition: firstResult.position.coordinates,\n },\n }),\n };\n};\n","import { geographyTypes, views } from '@tomtom-org/maps-sdk/core';\nimport { z } from 'zod';\nimport { hasLngLatSchema } from '../shared/schema/geometriesSchema';\n\nconst revGeocodeRequestMandatory = z.object({\n position: hasLngLatSchema,\n});\n\nconst revGeocodeRequestOptional = z.object({\n geographyType: z.array(z.enum(geographyTypes)).optional(),\n heading: z.number().min(-360).max(360).optional(),\n radiusMeters: z.number().positive().max(5_000_000).optional(),\n view: z.enum(views).optional(),\n});\n\n/**\n * @ignore\n */\nexport const revGeocodeRequestSchema = revGeocodeRequestMandatory.extend(revGeocodeRequestOptional.shape);\n","import type { GetObject, ServiceTemplate } from '../shared';\nimport { get } from '../shared/fetch';\nimport { buildRevGeoRequest } from './requestBuilder';\nimport { parseRevGeoResponse } from './responseParser';\nimport type { ReverseGeocodingResponse } from './reverseGeocoding';\nimport { revGeocodeRequestSchema } from './revGeocodeRequestSchema';\nimport type { ReverseGeocodingResponseAPI } from './types/apiTypes';\nimport type { ReverseGeocodingParams } from './types/reverseGeocodingParams';\n\n/**\n * Reverse Geocoding service template type.\n * @ignore\n */\nexport type ReverseGeocodingTemplate = ServiceTemplate<\n ReverseGeocodingParams,\n GetObject,\n ReverseGeocodingResponseAPI,\n ReverseGeocodingResponse\n>;\n\n/**\n * Reverse Geocoding service template main implementation.\n * @ignore\n */\nexport const reverseGeocodingTemplate: ReverseGeocodingTemplate = {\n requestValidation: { schema: revGeocodeRequestSchema },\n buildRequest: buildRevGeoRequest,\n getAPIVersion: () => 2,\n sendRequest: get,\n parseResponse: parseRevGeoResponse,\n};\n","import { buildRevGeoRequest } from './requestBuilder';\nimport { parseRevGeoResponse } from './responseParser';\nimport type { ReverseGeocodingTemplate } from './reverseGeocodingTemplate';\nimport { reverseGeocodingTemplate } from './reverseGeocodingTemplate';\n\nconst customize: {\n buildRevGeoRequest: typeof buildRevGeoRequest;\n parseRevGeoResponse: typeof parseRevGeoResponse;\n reverseGeocodingTemplate: ReverseGeocodingTemplate;\n} = {\n buildRevGeoRequest,\n parseRevGeoResponse,\n reverseGeocodingTemplate,\n};\nexport default customize;\n","import type { SectionType } from '@tomtom-org/maps-sdk/core';\nimport { getRoutePlanningLocationType, inputSectionTypesWithGuidance } from '@tomtom-org/maps-sdk/core';\nimport { z } from 'zod';\nimport { commonRoutingRequestSchema } from '../shared/schema/commonRoutingRequestSchema';\nimport {\n featureSchema,\n geometrySchema,\n hasLngLatSchema,\n lineStringCoordsSchema,\n} from '../shared/schema/geometriesSchema';\nimport type { SchemaRefinement } from '../shared/types/validation';\nimport type { CalculateRouteParams } from './types/calculateRouteParams';\n\nconst waypointLikeSchema = z.union([hasLngLatSchema, geometrySchema]);\nconst pathLikeSchema = z.union([lineStringCoordsSchema, featureSchema]);\n\nconst mandatorySchema = z.object({\n locations: z.array(z.union([waypointLikeSchema, pathLikeSchema])).min(1), // see calculateRouteLocationsRefinement\n});\n\nconst optionalSchema = z.object({\n computeAdditionalTravelTimeFor: z.enum(['none', 'all']).optional(),\n maxAlternatives: z.number().min(0).max(5).optional(),\n sectionTypes: z.array(z.enum(inputSectionTypesWithGuidance as [SectionType, ...SectionType[]])).optional(),\n});\n\nconst schema = commonRoutingRequestSchema.extend(mandatorySchema.extend(optionalSchema.shape).shape);\n\nconst locationsRefinement: SchemaRefinement<CalculateRouteParams> = {\n check: (data: CalculateRouteParams): boolean => {\n const types = data.locations.map(getRoutePlanningLocationType);\n const waypointCount = types.filter((type) => type === 'waypoint').length;\n const pathCount = types.filter((type) => type === 'path').length;\n // At least 2 waypoints, OR at least 1 path (reconstruction)\n return waypointCount >= 2 || pathCount >= 1;\n },\n message: 'At least 2 waypoints or 1 path (route reconstruction) is required.',\n};\n\n/**\n * @ignore\n */\nexport const routeRequestValidationConfig = { schema, refinements: [locationsRefinement] };\n","import {\n bboxFromGeoJSON,\n getPositionStrict,\n getRoutePlanningLocationType,\n type HasBBox,\n type LegSectionProps,\n type PathLike,\n type RoutePlanningLocation,\n type TomTomAPIHeaders,\n type WaypointLike,\n} from '@tomtom-org/maps-sdk/core';\nimport type { LineString, Point } from 'geojson';\nimport { isNil } from 'lodash-es';\nimport type {\n ChargingPreferencesKWH,\n ChargingPreferencesPCT,\n CombustionVehicleState,\n ElectricVehicleParams,\n ElectricVehicleStateKWH,\n ElectricVehicleStatePCT,\n FetchInput,\n VehicleParameters,\n} from '../shared';\nimport { buildCommonServiceRequestHeaders } from '../shared/request/requestBuildingUtils';\nimport type {\n CombustionEngineModel,\n ConsumptionModelEfficiency,\n ElectricEngineModel,\n SpeedToConsumptionRate,\n VehicleEngineType,\n} from '../shared/types/vehicleEngineParams';\nimport type { ExplicitVehicleModel } from '../shared/types/vehicleModel';\nimport type { AvoidAreasAPI, CalculateRoutePOSTDataAPI, LegRequestAPI } from './types/apiRequestTypes';\nimport type { CalculateRouteParams, GuidanceParams } from './types/calculateRouteParams';\n\nconst buildAvoidAreas = (avoidAreas: HasBBox[]): AvoidAreasAPI => ({\n rectangles: avoidAreas.map((rectangle) => {\n const bbox = bboxFromGeoJSON(rectangle);\n if (!bbox) throw new Error('Could not derive a bounding box from an avoidAreas rectangle');\n\n const [minLon, minLat, maxLon, maxLat] = bbox;\n return { type: 'Feature' as const, bbox: [minLon, minLat, maxLon, maxLat], geometry: null };\n }),\n});\n\nconst toGuidanceBody = (\n guidance: GuidanceParams,\n): { guidance: 'instructions'; instructionPhonetics: 'ipa' | 'lhp' } => ({\n guidance: 'instructions',\n instructionPhonetics: guidance.phonetics ? (guidance.phonetics.toLowerCase() as 'ipa' | 'lhp') : 'ipa',\n});\n\nconst buildSpeedConsumptionString = (rates: SpeedToConsumptionRate[]): string =>\n rates.map((rate) => `${rate.speedKMH},${rate.consumptionUnitsPer100KM}`).join(':');\n\n// V3 section types that are EXPLICIT — they must be individually listed in the Attributes header\n// to be included in the response, even when the parent `sections` is already requested.\n// `lanes` is also EXPLICIT but is guidance-only and handled separately below.\nconst EXPLICIT_SECTION_TYPES = ['tollVignette', 'roadShields', 'importantRoadStretch'] as const;\n\nconst buildAttributesHeader = (params: CalculateRouteParams): string => {\n const parts = ['summary', 'legs(summary,path)'];\n const sectionTypes = params.sectionTypes;\n // V3 returns all non-EXPLICIT section types when sections is requested.\n // Omit sections only when the caller explicitly passes an empty sectionTypes array.\n const requestSections = !sectionTypes || sectionTypes.length > 0;\n if (requestSections) {\n parts.push('sections');\n // EXPLICIT V3 section sub-attributes must be listed individually in the Attributes header.\n // When sectionTypes is undefined the customer wants everything, so request all EXPLICIT types\n // (preserving V2 behavior). When sectionTypes is a specific list, only add the requested ones.\n const wantAll = !sectionTypes;\n for (const type of EXPLICIT_SECTION_TYPES) {\n if (wantAll || sectionTypes.includes(type)) {\n parts.push(`sections.${type}`);\n }\n }\n // sections.lanes is EXPLICIT and only available with guidance.\n if (params.guidance && (wantAll || sectionTypes.includes('lanes'))) {\n parts.push('sections.lanes');\n }\n }\n // V3 dropped the per-representation `extendedRouteRepresentation` parameter: `progressPoints`\n // is all-or-nothing, so a non-empty `extendedRouteRepresentations` array coarsens to \"give me\n // progress points\". An explicitly empty array is the caller opting out, and saves the payload.\n if (params.extendedRouteRepresentations?.length !== 0) parts.push('progressPoints');\n // When guidance is requested, include the EXPLICIT instruction sub-attributes that back\n // fields the V2 SDK exposed: road-shield icon references, signpost exit icon\n // (→ signpostRoadShieldReferences), and the traffic-light offset.\n if (params.guidance) {\n parts.push(\n 'instructions(*,nextRoadInformation.roadShields.iconReference,previousRoadInformation.roadShields.iconReference,signpost.exitIconReference,distanceToPreviousTrafficLightInMeters)',\n );\n }\n // `roadShieldAtlasReference` is an EXPLICIT top-level attribute (sibling of `routes`), used to\n // build road-shield images; request it whenever guidance instructions are requested.\n const routesAttr = `routes(${parts.join(',')})`;\n return params.guidance ? `roadShieldAtlasReference,${routesAttr}` : routesAttr;\n};\n\n// ─── Regular route (V3 /routes/calculate) vehicle body + URL param helpers ───\n\nconst buildVehicleBody = (vehicle: VehicleParameters): Partial<CalculateRoutePOSTDataAPI> => {\n const result: Partial<CalculateRoutePOSTDataAPI> = {};\n const isElectric = (vehicle as ElectricVehicleParams).engineType === 'electric';\n if (isElectric) result.vehicleEngineType = 'electric';\n\n if (vehicle.model && !('variantId' in vehicle.model)) {\n const model = vehicle.model as ExplicitVehicleModel<VehicleEngineType>;\n if (model.dimensions?.weightKG) result.vehicleWeightInKilograms = model.dimensions.weightKG;\n }\n\n if (vehicle.state?.heading) result.vehicleHeadingInDegrees = vehicle.state.heading;\n\n if (vehicle.restrictions?.maxSpeedKMH) result.vehicleMaxSpeedInKilometersPerHour = vehicle.restrictions.maxSpeedKMH;\n\n return result;\n};\n\nconst setEfficiencyParams = (url: URL, efficiency: ConsumptionModelEfficiency): void => {\n if (!isNil(efficiency.acceleration))\n url.searchParams.set('accelerationEfficiency', String(efficiency.acceleration));\n\n if (!isNil(efficiency.deceleration))\n url.searchParams.set('decelerationEfficiency', String(efficiency.deceleration));\n\n if (!isNil(efficiency.uphill)) url.searchParams.set('uphillEfficiency', String(efficiency.uphill));\n\n if (!isNil(efficiency.downhill)) url.searchParams.set('downhillEfficiency', String(efficiency.downhill));\n};\n\n// Electric consumption + max-charge URL params shared by the regular (/routes/calculate) and LDEVR\n// (/calculateLongDistanceEVRoute) request builders.\nconst appendElectricConsumptionParams = (url: URL, engine: ElectricEngineModel): void => {\n const consumption = engine.consumption;\n if (consumption.speedsToConsumptionsKWH?.length)\n url.searchParams.set(\n 'constantSpeedConsumptionInkWhPerHundredkm',\n buildSpeedConsumptionString(consumption.speedsToConsumptionsKWH),\n );\n\n if (!isNil(consumption.auxiliaryPowerInkW))\n url.searchParams.set('auxiliaryPowerInkW', String(consumption.auxiliaryPowerInkW));\n\n if (!isNil(consumption.consumptionInKWHPerKMAltitudeGain))\n url.searchParams.set(\n 'consumptionInkWhPerkmAltitudeGain',\n String(consumption.consumptionInKWHPerKMAltitudeGain),\n );\n\n if (!isNil(consumption.recuperationInKWHPerKMAltitudeLoss))\n url.searchParams.set(\n 'recuperationInkWhPerkmAltitudeLoss',\n String(consumption.recuperationInKWHPerKMAltitudeLoss),\n );\n\n if (consumption.efficiency) setEfficiencyParams(url, consumption.efficiency);\n\n if (engine.charging?.maxChargeKWH) url.searchParams.set('maxChargeInkWh', String(engine.charging.maxChargeKWH));\n};\n\nconst appendElectricParams = (url: URL, engine: ElectricEngineModel, vehicle: VehicleParameters): void => {\n appendElectricConsumptionParams(url, engine);\n\n const kwhState = vehicle.state as ElectricVehicleStateKWH;\n const pctState = vehicle.state as ElectricVehicleStatePCT;\n if (kwhState?.currentChargeInkWh) {\n url.searchParams.set('currentChargeInkWh', String(kwhState.currentChargeInkWh));\n } else if (pctState?.currentChargePCT && engine.charging?.maxChargeKWH) {\n url.searchParams.set(\n 'currentChargeInkWh',\n String((engine.charging.maxChargeKWH * pctState.currentChargePCT) / 100),\n );\n }\n};\n\nconst appendCombustionParams = (url: URL, engine: CombustionEngineModel, vehicle: VehicleParameters): void => {\n const consumption = engine.consumption;\n if (consumption.speedsToConsumptionsLiters?.length)\n url.searchParams.set(\n 'constantSpeedConsumptionInLitersPerHundredkm',\n buildSpeedConsumptionString(consumption.speedsToConsumptionsLiters),\n );\n\n if (!isNil(consumption.auxiliaryPowerInLitersPerHour))\n url.searchParams.set('auxiliaryPowerInLitersPerHour', String(consumption.auxiliaryPowerInLitersPerHour));\n\n if (!isNil(consumption.fuelEnergyDensityInMJoulesPerLiter))\n url.searchParams.set(\n 'fuelEnergyDensityInMJoulesPerLiter',\n String(consumption.fuelEnergyDensityInMJoulesPerLiter),\n );\n\n if (consumption.efficiency) setEfficiencyParams(url, consumption.efficiency);\n\n const combustionState = vehicle.state as CombustionVehicleState;\n if (combustionState?.currentFuelInLiters)\n url.searchParams.set('currentFuelInLiters', String(combustionState.currentFuelInLiters));\n};\n\nconst appendConsumptionParams = (url: URL, vehicle: VehicleParameters): void => {\n if (!vehicle.model || 'variantId' in vehicle.model) return;\n\n const model = vehicle.model as ExplicitVehicleModel<VehicleEngineType>;\n if (!model.engine) return;\n\n if ((vehicle as ElectricVehicleParams).engineType === 'electric') {\n appendElectricParams(url, model.engine as ElectricEngineModel, vehicle);\n } else {\n appendCombustionParams(url, model.engine as CombustionEngineModel, vehicle);\n }\n};\n\n// ─── LDEVR (/calculateLongDistanceEVRoute) URL param helpers ─────────────────\n\nconst getLDEVRMaxCharge = (vehicle: VehicleParameters): number | undefined => {\n if (!vehicle.model || 'variantId' in vehicle.model) return undefined;\n\n return ((vehicle.model as ExplicitVehicleModel<VehicleEngineType>).engine as ElectricEngineModel | undefined)\n ?.charging?.maxChargeKWH;\n};\n\nconst appendLDEVRStateParams = (url: URL, vehicle: VehicleParameters): void => {\n if (!vehicle.state) return;\n\n const kwhState = vehicle.state as ElectricVehicleStateKWH;\n const pctState = vehicle.state as ElectricVehicleStatePCT;\n if (kwhState.currentChargeInkWh) {\n url.searchParams.set('currentChargeInkWh', String(kwhState.currentChargeInkWh));\n return;\n }\n\n if (!pctState.currentChargePCT) return;\n\n const maxCharge = getLDEVRMaxCharge(vehicle);\n if (maxCharge) url.searchParams.set('currentChargeInkWh', String((maxCharge * pctState.currentChargePCT) / 100));\n};\n\nconst appendLDEVRChargingPrefParams = (url: URL, vehicle: VehicleParameters): void => {\n const chargingPrefs = (vehicle as ElectricVehicleParams).preferences?.chargingPreferences;\n if (!chargingPrefs) return;\n\n const kwhPrefs = chargingPrefs as ChargingPreferencesKWH;\n const pctPrefs = chargingPrefs as ChargingPreferencesPCT;\n if (!isNil(kwhPrefs.minChargeAtDestinationInkWh) || !isNil(kwhPrefs.minChargeAtChargingStopsInkWh)) {\n if (!isNil(kwhPrefs.minChargeAtDestinationInkWh))\n url.searchParams.set('minChargeAtDestinationInkWh', String(kwhPrefs.minChargeAtDestinationInkWh));\n\n if (!isNil(kwhPrefs.minChargeAtChargingStopsInkWh))\n url.searchParams.set('minChargeAtChargingStopsInkWh', String(kwhPrefs.minChargeAtChargingStopsInkWh));\n\n return;\n }\n\n const maxCharge = getLDEVRMaxCharge(vehicle);\n if (!maxCharge) return;\n\n if (!isNil(pctPrefs.minChargeAtDestinationPCT))\n url.searchParams.set(\n 'minChargeAtDestinationInkWh',\n String((maxCharge * pctPrefs.minChargeAtDestinationPCT) / 100),\n );\n\n if (!isNil(pctPrefs.minChargeAtChargingStopsPCT))\n url.searchParams.set(\n 'minChargeAtChargingStopsInkWh',\n String((maxCharge * pctPrefs.minChargeAtChargingStopsPCT) / 100),\n );\n};\n\nconst appendLDEVRVehicleParams = (url: URL, vehicle: VehicleParameters | undefined): void => {\n if (!vehicle) return;\n\n if (!vehicle.model) return;\n\n if ('variantId' in vehicle.model) {\n url.searchParams.set('vehicleModelId', vehicle.model.variantId);\n } else {\n const model = vehicle.model as ExplicitVehicleModel<VehicleEngineType>;\n if (model.engine) appendElectricConsumptionParams(url, model.engine as ElectricEngineModel);\n }\n appendLDEVRStateParams(url, vehicle);\n appendLDEVRChargingPrefParams(url, vehicle);\n};\n\nconst isLDEVRRequest = (params: CalculateRouteParams): boolean =>\n !!(params.vehicle as ElectricVehicleParams | undefined)?.preferences?.chargingPreferences;\n\n// ─── Route reconstruction helpers ────────────────────────────────────────────\n\ntype Stop = { coordinate: [number, number]; legToNext?: LegRequestAPI };\n\nconst getPathCoordinates = (pathLike: PathLike): [number, number][] => {\n if (Array.isArray(pathLike)) return pathLike as [number, number][];\n\n return pathLike.geometry.coordinates as [number, number][];\n};\n\nconst getPathLegSections = (pathLike: PathLike): LegSectionProps[] | undefined => {\n if (Array.isArray(pathLike)) return undefined;\n\n return pathLike.properties?.sections?.leg;\n};\n\nconst expandPathToStops = (pathLike: PathLike): Stop[] => {\n const coordinates = getPathCoordinates(pathLike);\n const legSections = getPathLegSections(pathLike);\n if (\n legSections &&\n legSections.length > 1 &&\n legSections.every((section) => section.startPointIndex != null && section.endPointIndex != null)\n ) {\n const stops: Stop[] = legSections.map((section) => {\n const start = section.startPointIndex ?? 0;\n const end = section.endPointIndex ?? 0;\n return {\n coordinate: coordinates[start],\n legToNext: {\n path: {\n type: 'LineString',\n coordinates: coordinates.slice(start, end + 1),\n },\n },\n };\n });\n stops.push({\n coordinate: coordinates[legSections.at(-1)?.endPointIndex ?? coordinates.length - 1],\n });\n return stops;\n }\n return [\n {\n coordinate: coordinates[0],\n legToNext: { path: { type: 'LineString', coordinates } as LineString },\n },\n { coordinate: coordinates.at(-1) ?? coordinates[0] },\n ];\n};\n\nconst flattenLocations = (\n locations: RoutePlanningLocation[],\n useEntryPoint?: CalculateRouteParams['useEntryPoints'],\n): Stop[] => {\n const result: Stop[] = [];\n for (const location of locations) {\n if (getRoutePlanningLocationType(location) === 'waypoint') {\n const [longitude, latitude] = getPositionStrict(location as WaypointLike, { useEntryPoint });\n result.push({ coordinate: [longitude, latitude] });\n } else {\n result.push(...expandPathToStops(location as PathLike));\n }\n }\n return result;\n};\n\nconst buildRoutePlanningLocations = (allStops: Stop[]) => {\n const origin: Point = { type: 'Point', coordinates: allStops[0].coordinate };\n const destination: Point = {\n type: 'Point',\n coordinates: (allStops.at(-1) ?? allStops[0]).coordinate,\n };\n const middleStops = allStops.slice(1, -1);\n return {\n origin,\n destination,\n ...(middleStops.length && {\n waypoints: {\n type: 'MultiPoint' as const,\n coordinates: middleStops.map((stop) => stop.coordinate),\n },\n }),\n };\n};\n\n// ─── Main exported function helpers ──────────────────────────────────────────\n\ntype CommonBodyFields = ReturnType<typeof buildCommonBodyFields>;\n\nconst buildCommonBodyFields = (params: CalculateRouteParams) => ({\n ...(params.costModel?.routeType && { routeType: params.costModel.routeType }),\n ...(params.costModel?.traffic && { traffic: params.costModel.traffic }),\n ...(params.costModel?.avoid?.length && { avoids: params.costModel.avoid as string[] }),\n ...(params.travelMode && { travelMode: params.travelMode }),\n ...(params.maxAlternatives && { maxPathAlternativeRoutes: params.maxAlternatives }),\n ...(params.when?.option === 'departAt' &&\n params.when.date && { departureDateTime: params.when.date.toISOString() }),\n ...(params.when?.option === 'arriveBy' && params.when.date && { arrivalDateTime: params.when.date.toISOString() }),\n ...(params.guidance && toGuidanceBody(params.guidance)),\n ...(params.costModel?.avoidAreas?.length && { avoidAreas: buildAvoidAreas(params.costModel.avoidAreas) }),\n});\n\nconst buildRequestHeaders = (params: CalculateRouteParams): TomTomAPIHeaders => ({\n ...buildCommonServiceRequestHeaders(params),\n Attributes: buildAttributesHeader(params),\n});\n\nconst buildLDEVRRequest = (\n params: CalculateRouteParams,\n routePlanningLocations: ReturnType<typeof buildRoutePlanningLocations>,\n commonBodyFields: CommonBodyFields,\n headers: TomTomAPIHeaders,\n): FetchInput<CalculateRoutePOSTDataAPI> => {\n const baseURL =\n params.customServiceBaseURL ?? `${params.commonBaseURL}/maps/orbis/routing/calculateLongDistanceEVRoute`;\n const url = new URL(baseURL);\n appendLDEVRVehicleParams(url, params.vehicle);\n\n const chargingModel =\n params.vehicle?.model && !('variantId' in params.vehicle.model)\n ? (\n (params.vehicle.model as ExplicitVehicleModel<VehicleEngineType>).engine as\n | ElectricEngineModel\n | undefined\n )?.charging\n : undefined;\n\n const body: CalculateRoutePOSTDataAPI = {\n routePlanningLocations,\n ...commonBodyFields,\n ...(chargingModel && {\n chargingParameters: {\n ...(chargingModel.batteryCurve?.length && { batteryCurve: chargingModel.batteryCurve }),\n ...(chargingModel.chargingConnectors?.length && {\n chargingConnectors: [...chargingModel.chargingConnectors],\n }),\n ...(!isNil(chargingModel.chargingTimeOffsetInSec) && {\n chargingTimeOffsetInSec: chargingModel.chargingTimeOffsetInSec,\n }),\n },\n }),\n vehicleEngineType: 'electric',\n ...(params.vehicle?.model &&\n !('variantId' in params.vehicle.model) && {\n vehicleWeightInKilograms: (params.vehicle.model as ExplicitVehicleModel<VehicleEngineType>).dimensions\n ?.weightKG,\n }),\n ...(params.vehicle?.state?.heading && { vehicleHeadingInDegrees: params.vehicle.state.heading }),\n };\n\n return { method: 'POST', url, data: body, headers };\n};\n\n// ─── Main exported function ───────────────────────────────────────────────────\n\n/**\n * Default function for building calculate route request from {@link CalculateRouteParams}\n * targeting the OrbisV3 routing API.\n * @param params The calculate route parameters, with global configuration already merged into them.\n */\nexport const buildCalculateRouteRequest = (params: CalculateRouteParams): FetchInput<CalculateRoutePOSTDataAPI> => {\n const hasPathLocations = params.locations.map(getRoutePlanningLocationType).includes('path');\n const allStops = flattenLocations(params.locations, params.useEntryPoints);\n const routePlanningLocations = buildRoutePlanningLocations(allStops);\n const commonBodyFields = buildCommonBodyFields(params);\n const headers = buildRequestHeaders(params);\n if (isLDEVRRequest(params)) {\n return buildLDEVRRequest(params, routePlanningLocations, commonBodyFields, headers);\n }\n\n const baseURL = params.customServiceBaseURL ?? `${params.commonBaseURL}/maps/orbis/routing/routes/calculate`;\n const url = new URL(baseURL);\n if (params.vehicle) appendConsumptionParams(url, params.vehicle);\n // V3 renamed computeAdditionalTravelTimeFor → computeTravelTimeFor\n if (params.computeAdditionalTravelTimeFor) {\n url.searchParams.set('computeTravelTimeFor', params.computeAdditionalTravelTimeFor);\n }\n\n const legs = hasPathLocations ? allStops.slice(0, -1).map((stop) => stop.legToNext ?? {}) : undefined;\n\n const body: CalculateRoutePOSTDataAPI = {\n routePlanningLocations,\n ...commonBodyFields,\n ...(legs && { legs }),\n ...(params.vehicle && buildVehicleBody(params.vehicle)),\n };\n\n return { method: 'POST', url, data: body, headers };\n};\n","import {\n BBox,\n bboxFromGeoJSON,\n type ChargingStop,\n type CountrySectionProps,\n type CurrentType,\n type DelayMagnitude,\n type DrivingSide,\n type Guidance,\n generateId,\n type ImportantRoadStretchProps,\n type Instruction,\n type Landmark,\n type LaneDirection,\n type LaneSectionProps,\n type LegSectionProps,\n type LegSummary,\n type Maneuver,\n type PossibleLaneDirection,\n type PossibleLaneSeparator,\n type RoadInformation,\n type RoadInformationProperty,\n type RoadShield,\n type RoadShieldReference,\n type RoadShieldSectionProps,\n type Route,\n type RouteSummary,\n type Routes,\n type SectionProps,\n type SectionsProps,\n type Signpost,\n type SpeedLimitSectionProps,\n type TextWithPhonetics,\n type TollPaymentType,\n type TrafficIncidentCategory,\n type TrafficIncidentTEC,\n type TrafficSectionProps,\n} from '@tomtom-org/maps-sdk/core';\nimport type { LineString, Position } from 'geojson';\nimport { isNil, omit } from 'lodash-es';\nimport { toChargingSpeed } from '../shared/ev';\nimport { toIso3 } from '../shared/iso2ToIso3';\nimport { ExplicitVehicleModel } from '../shared/types/vehicleModel';\nimport type {\n BasicSectionAPI,\n CalculateRouteResponseAPI,\n ChargingStopAPI,\n CountrySectionAPI,\n CurrentTypeAPI,\n ImportantRoadStretchSectionAPI,\n InstructionAPI,\n InstructionRoadInformationAPI,\n InstructionRoadShieldAPI,\n LanesSectionAPI,\n LegAPI,\n ProgressPointAPI,\n RouteAPI,\n SpeedLimitSectionAPI,\n SummaryAPI,\n TextWithPhoneticsAPI,\n TrafficSectionAPI,\n TravelModeSectionAPI,\n} from './types/apiResponseTypes';\nimport type { CalculateRouteParams } from './types/calculateRouteParams';\n\nconst toCurrentType = (apiCurrentType: CurrentTypeAPI): CurrentType | undefined => {\n switch (apiCurrentType) {\n case 'Direct_Current':\n return 'DC';\n case 'Alternating_Current_1_Phase':\n return 'AC1';\n case 'Alternating_Current_3_Phase':\n return 'AC3';\n default:\n return undefined;\n }\n};\n\nconst toChargingStop = (\n chargingInformationAtEndOfLeg: ChargingStopAPI,\n maxChargeKWH: number | undefined,\n): ChargingStop => {\n const chargingConnectionInfo = chargingInformationAtEndOfLeg.chargingConnectionInfo;\n const chargingParkLocation = chargingInformationAtEndOfLeg.chargingParkLocation;\n const coordinates = [chargingParkLocation.coordinate.longitude, chargingParkLocation.coordinate.latitude];\n\n const addressParts = [chargingParkLocation.street, chargingParkLocation.houseNumber].filter(Boolean);\n const freeformAddress = addressParts.length > 0 ? addressParts.join(', ') : '';\n\n return {\n type: 'Feature',\n id: chargingInformationAtEndOfLeg.chargingParkId,\n geometry: { type: 'Point', coordinates },\n properties: {\n ...omit(chargingInformationAtEndOfLeg, ['chargingConnectionInfo', 'chargingParkLocation']),\n type: 'POI',\n address: {\n freeformAddress,\n ...(chargingParkLocation.street && { streetName: chargingParkLocation.street }),\n ...(chargingParkLocation.houseNumber && { streetNumber: chargingParkLocation.houseNumber }),\n ...(chargingParkLocation.city && { municipality: chargingParkLocation.city }),\n ...(chargingParkLocation.region && { countrySubdivision: chargingParkLocation.region }),\n ...(chargingParkLocation.postalCode && { postalCode: chargingParkLocation.postalCode }),\n ...(chargingParkLocation.country && { country: chargingParkLocation.country }),\n },\n ...(chargingConnectionInfo && {\n chargingConnectionInfo: {\n plugType: chargingConnectionInfo.chargingPlugType,\n currentInA: chargingConnectionInfo.chargingCurrentInA,\n voltageInV: chargingConnectionInfo.chargingVoltageInV,\n chargingPowerInkW: chargingConnectionInfo.chargingPowerInkW,\n currentType: toCurrentType(chargingConnectionInfo.chargingCurrentType),\n chargingSpeed: toChargingSpeed(chargingConnectionInfo.chargingPowerInkW),\n },\n }),\n ...(maxChargeKWH && {\n targetChargeInPCT: (100 * chargingInformationAtEndOfLeg.targetChargeInkWh) / maxChargeKWH,\n }),\n ...(chargingInformationAtEndOfLeg.chargingParkPowerInkW && {\n chargingParkSpeed: toChargingSpeed(chargingInformationAtEndOfLeg.chargingParkPowerInkW),\n }),\n },\n };\n};\n\nconst parseSummary = (apiSummary: SummaryAPI, params: CalculateRouteParams): RouteSummary | LegSummary => {\n const maxChargeKWH = (params?.vehicle?.model as ExplicitVehicleModel<'electric'>)?.engine?.charging?.maxChargeKWH;\n return {\n lengthInMeters: apiSummary.lengthInMeters,\n travelTimeInSeconds: apiSummary.travelDurationInSeconds,\n trafficDelayInSeconds: apiSummary.trafficDelayDurationInSeconds ?? 0,\n trafficLengthInMeters: apiSummary.trafficLengthInMeters ?? 0,\n historicTrafficTravelTimeInSeconds: apiSummary.historicTrafficTravelTimeInSeconds,\n liveTrafficIncidentsTravelTimeInSeconds: apiSummary.liveTrafficIncidentsTravelTimeInSeconds,\n noTrafficTravelTimeInSeconds: apiSummary.noTrafficTravelTimeInSeconds,\n departureTime: new Date(apiSummary.departureDateTime),\n arrivalTime: new Date(apiSummary.arrivalDateTime),\n deviationDistanceInMeters: apiSummary.deviationDistanceInMeters,\n deviationTimeInSeconds: apiSummary.deviationDurationInSeconds,\n ...(apiSummary.deviationPoint && {\n deviationPoint: apiSummary.deviationPoint.coordinates,\n }),\n fuelConsumptionInLiters: apiSummary.fuelConsumptionInLiters,\n totalChargingTimeInSeconds: apiSummary.totalChargingTimeInSeconds,\n batteryConsumptionInkWh: apiSummary.batteryConsumptionInKilowattHours ?? apiSummary.batteryConsumptionInkWh,\n ...(maxChargeKWH &&\n (() => {\n const batteryConsumption =\n apiSummary.batteryConsumptionInKilowattHours ?? apiSummary.batteryConsumptionInkWh;\n return batteryConsumption\n ? { batteryConsumptionInPCT: (100 * batteryConsumption) / maxChargeKWH }\n : undefined;\n })()),\n remainingChargeAtArrivalInkWh: apiSummary.remainingChargeAtArrivalInkWh,\n ...(maxChargeKWH &&\n apiSummary.remainingChargeAtArrivalInkWh && {\n remainingChargeAtArrivalInPCT: (100 * apiSummary.remainingChargeAtArrivalInkWh) / maxChargeKWH,\n }),\n ...(apiSummary.chargingInformationAtEndOfLeg && {\n chargingInformationAtEndOfLeg: toChargingStop(apiSummary.chargingInformationAtEndOfLeg, maxChargeKWH),\n }),\n };\n};\n\nconst parseRoutePath = (apiRouteLegs: LegAPI[]): LineString => ({\n type: 'LineString',\n coordinates: apiRouteLegs.flatMap((leg) => leg.path.coordinates),\n});\n\nconst parseLegSectionProps = (apiLegs: LegAPI[], params: CalculateRouteParams): LegSectionProps[] =>\n apiLegs.reduce<LegSectionProps[]>((accumulator, leg, legIndex) => {\n const legCoordinateCount = leg.path.coordinates.length;\n const lastEndIndex = legIndex === 0 ? 0 : accumulator[legIndex - 1]?.endPointIndex;\n let endPointIndex: number | undefined;\n if (!isNil(lastEndIndex)) {\n endPointIndex =\n lastEndIndex === 0 ? Math.max(legCoordinateCount - 1, 0) : lastEndIndex + legCoordinateCount;\n }\n accumulator.push({\n ...(!isNil(lastEndIndex) && { startPointIndex: lastEndIndex }),\n ...(endPointIndex !== undefined && { endPointIndex }),\n summary: parseSummary(leg.summary, params),\n id: generateId(),\n });\n return accumulator;\n }, []);\n\nconst toSectionProps = (section: BasicSectionAPI): SectionProps => ({\n id: generateId(),\n startPointIndex: section.startPathIndex,\n endPointIndex: section.endPathIndex,\n});\n\n/**\n * Map an OrbisV3 traffic section to the SDK's semantic traffic incident categories.\n * Uses TEC main cause codes when available, falling back to the API's coarser `iconCategory`.\n * @ignore\n */\nexport const toTrafficCategories = (section: TrafficSectionAPI): TrafficIncidentCategory[] => {\n if (section.tec?.causes?.length) {\n return section.tec.causes.map((cause) => {\n switch (cause.mainCauseCode) {\n case 1:\n return 'jam';\n case 2:\n return 'accident';\n case 3:\n return 'roadworks';\n case 4:\n return 'narrow-lanes';\n case 5:\n return 'road-closed';\n case 9:\n return 'danger';\n case 11:\n return 'animals-on-road';\n case 13:\n return 'broken-down-vehicle';\n case 16:\n return 'lane-closed';\n case 17:\n return 'wind';\n case 18:\n return 'fog';\n case 19:\n return 'rain';\n case 22:\n return 'frost';\n case 23:\n return 'flooding';\n default:\n return 'other';\n }\n });\n }\n switch (section.iconCategory) {\n case 'jam':\n return ['jam'];\n case 'accident':\n return ['accident'];\n case 'brokenDownVehicle':\n return ['broken-down-vehicle'];\n case 'dangerousConditions':\n return ['danger'];\n case 'flooding':\n return ['flooding'];\n case 'fog':\n return ['fog'];\n case 'ice':\n return ['frost'];\n case 'laneClosed':\n return ['lane-closed'];\n case 'rain':\n return ['rain'];\n case 'roadClosed':\n return ['road-closed'];\n case 'roadWorks':\n return ['roadworks'];\n case 'wind':\n return ['wind'];\n default:\n return ['other'];\n }\n};\n\ntype BasicSectionKey =\n | 'carTrain'\n | 'ferry'\n | 'tunnel'\n | 'motorway'\n | 'pedestrian'\n | 'toll'\n | 'carpool'\n | 'urban'\n | 'unpaved'\n | 'lowEmissionZone';\n\nconst parseSpeedLimitSections = (speedLimitSections: SpeedLimitSectionAPI[]): SpeedLimitSectionProps[] =>\n // V3 carries speeds under `speedRestrictions`; the SDK exposes only the maximum limit.\n speedLimitSections\n .map((speedLimit): SpeedLimitSectionProps | undefined => {\n const max = speedLimit.speedRestrictions?.find((r) => r.type === 'maximum')?.inKilometersPerHour;\n return isNil(max) ? undefined : { ...toSectionProps(speedLimit), maxSpeedLimitInKmh: max };\n })\n .filter((speedLimit): speedLimit is SpeedLimitSectionProps => !!speedLimit);\n\nconst parseLaneSections = (laneSections: LanesSectionAPI[]): LaneSectionProps[] =>\n // V3 lane directions/separators are camelCase strings; map to the SDK's UPPER_SNAKE enums.\n laneSections.map(\n (lanesSection): LaneSectionProps => ({\n ...toSectionProps(lanesSection),\n lanes: (lanesSection.lanes ?? []).map(\n (lane): LaneDirection => ({\n directions: lane.directions.map(\n (direction) => V3_LANE_DIRECTION_MAP[direction] ?? (direction as PossibleLaneDirection),\n ),\n ...(lane.follow && {\n follow: V3_LANE_DIRECTION_MAP[lane.follow] ?? (lane.follow as PossibleLaneDirection),\n }),\n }),\n ),\n laneSeparators: (lanesSection.laneSeparators ?? []).map(\n (separator) => V3_LANE_SEPARATOR_MAP[separator] ?? (separator as PossibleLaneSeparator),\n ),\n properties: lanesSection.properties,\n }),\n );\n\nconst parseImportantRoadStretchSections = (\n importantRoadStretchSections: ImportantRoadStretchSectionAPI[],\n): ImportantRoadStretchProps[] =>\n importantRoadStretchSections.map(\n (importantRoadStretch): ImportantRoadStretchProps => ({\n ...toSectionProps(importantRoadStretch),\n index: importantRoadStretch.importantRoadStretchIndex,\n ...(importantRoadStretch.streetName && { streetName: importantRoadStretch.streetName.text }),\n ...(importantRoadStretch.roadNumbers?.length && {\n roadNumbers: importantRoadStretch.roadNumbers.map((roadNumber) => roadNumber.text),\n }),\n }),\n );\n\n// V3 always returns every section type when sections are requested. Restore the legacy filtering by\n// dropping the section keys the caller did not ask for (the always-present `leg` is kept).\nconst applyRequestedSectionFilter = (\n result: SectionsProps,\n sectionTypes: CalculateRouteParams['sectionTypes'],\n): void => {\n if (!sectionTypes) return;\n const requested = new Set<string>(sectionTypes);\n for (const key of Object.keys(result) as (keyof SectionsProps)[]) {\n if (key !== 'leg' && !requested.has(key)) {\n delete result[key];\n }\n }\n};\n\nconst parseSections = (apiRoute: RouteAPI, params: CalculateRouteParams): SectionsProps => {\n const result: SectionsProps = {\n leg: parseLegSectionProps(apiRoute.legs, params),\n };\n\n const sections = apiRoute.sections;\n if (!sections) return result;\n\n const addBasic = (key: BasicSectionKey, items?: BasicSectionAPI[]) => {\n if (items?.length) result[key] = items.map(toSectionProps);\n };\n\n addBasic('carTrain', sections.carTrain);\n addBasic('ferry', sections.ferry);\n addBasic('tunnel', sections.tunnel);\n addBasic('motorway', sections.motorway);\n addBasic('pedestrian', sections.pedestrian);\n addBasic('toll', sections.toll);\n addBasic('carpool', sections.carpool);\n addBasic('urban', sections.urban);\n addBasic('unpaved', sections.unpaved);\n addBasic('lowEmissionZone', sections.lowEmissionZone);\n\n if (sections.country?.length) {\n result.country = sections.country.map((country: CountrySectionAPI) => ({\n ...toSectionProps(country),\n countryCodeISO3: toIso3(country.countryCodeIso2 ?? ''),\n }));\n }\n\n if (sections.travelMode?.length) {\n result.vehicleRestricted = sections.travelMode\n .filter((travelModeSection: TravelModeSectionAPI) => travelModeSection.travelMode === 'other')\n .map(toSectionProps);\n }\n\n if (sections.traffic?.length) {\n result.traffic = sections.traffic.map(\n (traffic: TrafficSectionAPI): TrafficSectionProps => ({\n ...toSectionProps(traffic),\n delayInSeconds: traffic.delayDurationInSeconds,\n effectiveSpeedInKmh: traffic.effectiveSpeedInKilometersPerHour,\n categories: toTrafficCategories(traffic),\n magnitudeOfDelay: (traffic.delayMagnitude === 'undefined'\n ? 'indefinite'\n : traffic.delayMagnitude) as DelayMagnitude,\n tec: traffic.tec as TrafficIncidentTEC,\n }),\n );\n }\n\n if (sections.speedLimit?.length) {\n result.speedLimit = parseSpeedLimitSections(sections.speedLimit);\n }\n\n if (sections.lanes?.length) {\n result.lanes = parseLaneSections(sections.lanes);\n }\n\n if (sections.roadShields?.length) {\n result.roadShields = sections.roadShields.map(\n (roadShield): RoadShieldSectionProps => ({\n ...toSectionProps(roadShield),\n roadShieldReferences: roadShield.roadShieldReferences as RoadShieldReference[],\n }),\n );\n }\n\n if (sections.tollVignette?.length) {\n result.tollVignette = sections.tollVignette.map(\n (tollVignette: CountrySectionAPI): CountrySectionProps => ({\n ...toSectionProps(tollVignette),\n countryCodeISO3: toIso3(tollVignette.countryCodeIso2 ?? ''),\n }),\n );\n }\n\n if (sections.importantRoadStretch?.length) {\n result.importantRoadStretch = parseImportantRoadStretchSections(sections.importantRoadStretch);\n }\n\n applyRequestedSectionFilter(result, params.sectionTypes);\n\n return result;\n};\n\nconst DELTA = 0.0001;\nconst similar = (pointA: Position, pointB: Position): boolean =>\n Math.abs(pointA[0] - pointB[0]) < DELTA && Math.abs(pointA[1] - pointB[1]) < DELTA;\n\n// V3 maneuver camelCase JSON values → SDK UPPER_SNAKE_CASE Maneuver type.\n// The two renamed cases: continueStraight → STRAIGHT, passTollgate → TOLLGATE.\nconst V3_MANEUVER_MAP: Record<string, Maneuver> = {\n depart: 'DEPART',\n waypointLeft: 'WAYPOINT_LEFT',\n waypointRight: 'WAYPOINT_RIGHT',\n waypointReached: 'WAYPOINT_REACHED',\n waypointAhead: 'WAYPOINT_AHEAD',\n arrive: 'ARRIVE',\n arriveLeft: 'ARRIVE_LEFT',\n arriveRight: 'ARRIVE_RIGHT',\n arriveAhead: 'ARRIVE_AHEAD',\n continueStraight: 'STRAIGHT',\n keepLeft: 'KEEP_LEFT',\n keepRight: 'KEEP_RIGHT',\n turnSlightRight: 'SLIGHT_RIGHT',\n turnRight: 'TURN_RIGHT',\n turnSharpRight: 'SHARP_RIGHT',\n turnSlightLeft: 'SLIGHT_LEFT',\n turnLeft: 'TURN_LEFT',\n turnSharpLeft: 'SHARP_LEFT',\n makeUTurn: 'MAKE_UTURN',\n roundaboutStraight: 'ROUNDABOUT_STRAIGHT',\n roundaboutSharpRight: 'ROUNDABOUT_SHARP_RIGHT',\n roundaboutRight: 'ROUNDABOUT_RIGHT',\n roundaboutSlightRight: 'ROUNDABOUT_SLIGHT_RIGHT',\n roundaboutSlightLeft: 'ROUNDABOUT_SLIGHT_LEFT',\n roundaboutLeft: 'ROUNDABOUT_LEFT',\n roundaboutSharpLeft: 'ROUNDABOUT_SHARP_LEFT',\n roundaboutBack: 'ROUNDABOUT_BACK',\n exitRoundabout: 'EXIT_ROUNDABOUT',\n exitMotorwayLeft: 'EXIT_MOTORWAY_LEFT',\n exitMotorwayRight: 'EXIT_MOTORWAY_RIGHT',\n switchMotorwayLeft: 'SWITCH_MOTORWAY_LEFT',\n switchMotorwayRight: 'SWITCH_MOTORWAY_RIGHT',\n enterCarpoolLaneLeft: 'ENTER_CARPOOL_LANE_LEFT',\n enterCarpoolLaneRight: 'ENTER_CARPOOL_LANE_RIGHT',\n exitCarpoolLaneLeft: 'EXIT_CARPOOL_LANE_LEFT',\n exitCarpoolLaneRight: 'EXIT_CARPOOL_LANE_RIGHT',\n mergeLeftLane: 'MERGE_LEFT_LANE',\n mergeRightLane: 'MERGE_RIGHT_LANE',\n takeShipFerry: 'TAKE_SHIP_FERRY',\n takeCarTrain: 'TAKE_CAR_TRAIN',\n leaveShipFerry: 'LEAVE_SHIP_FERRY',\n leaveCarTrain: 'LEAVE_CAR_TRAIN',\n crossBorder: 'CROSS_BORDER',\n passTollgate: 'TOLLGATE',\n};\n\nconst V3_ROAD_PROPERTY_MAP: Record<string, RoadInformationProperty> = {\n urban: 'URBAN',\n motorway: 'MOTORWAY',\n controlledAccess: 'CONTROLLED_ACCESS',\n};\n\n// V3 landmark camelCase JSON values → SDK UPPER_SNAKE_CASE Landmark type.\nconst V3_LANDMARK_MAP: Record<string, Landmark> = {\n endOfRoad: 'END_OF_ROAD',\n atTrafficLight: 'AT_TRAFFIC_LIGHT',\n onToBridge: 'ON_TO_BRIDGE',\n onBridge: 'ON_BRIDGE',\n afterBridge: 'AFTER_BRIDGE',\n intoTunnel: 'INTO_TUNNEL',\n insideTunnel: 'INSIDE_TUNNEL',\n afterTunnel: 'AFTER_TUNNEL',\n};\n\n// V3 toll payment camelCase JSON values → SDK UPPER_SNAKE_CASE TollPaymentType.\nconst V3_TOLL_PAYMENT_MAP: Record<string, TollPaymentType> = {\n cashCoinsAndBills: 'CASH_COINS_AND_BILLS',\n cashBillsOnly: 'CASH_BILLS_ONLY',\n cashCoinsOnly: 'CASH_COINS_ONLY',\n cashExactChange: 'CASH_EXACT_CHANGE',\n creditCard: 'CREDIT_CARD',\n debitCard: 'DEBIT_CARD',\n travelCard: 'TRAVEL_CARD',\n etc: 'ETC',\n etcTransponder: 'ETC_TRANSPONDER',\n etcVideoCamera: 'ETC_VIDEO_CAMERA',\n subscription: 'SUBSCRIPTION',\n};\n\n// V3 lane direction camelCase JSON values → SDK UPPER_SNAKE_CASE PossibleLaneDirection.\nconst V3_LANE_DIRECTION_MAP: Record<string, PossibleLaneDirection> = {\n straight: 'STRAIGHT',\n slightRight: 'SLIGHT_RIGHT',\n right: 'RIGHT',\n sharpRight: 'SHARP_RIGHT',\n rightUTurn: 'RIGHT_U_TURN',\n slightLeft: 'SLIGHT_LEFT',\n left: 'LEFT',\n sharpLeft: 'SHARP_LEFT',\n leftUTurn: 'LEFT_U_TURN',\n};\n\n// V3 lane separator camelCase JSON values → SDK UPPER_SNAKE_CASE PossibleLaneSeparator.\nconst V3_LANE_SEPARATOR_MAP: Record<string, PossibleLaneSeparator> = {\n unknown: 'UNKNOWN',\n noMarking: 'NO_MARKING',\n longDashed: 'LONG_DASHED',\n doubleSolid: 'DOUBLE_SOLID',\n singleSolid: 'SINGLE_SOLID',\n solidDashed: 'SOLID_DASHED',\n dashedSolid: 'DASHED_SOLID',\n shortDashed: 'SHORT_DASHED',\n shadedAreaMarking: 'SHADED_AREA_MARKING',\n dashedBlocks: 'DASHED_BLOCKS',\n doubleDashed: 'DOUBLE_DASHED',\n crossingAlert: 'CROSSING_ALERT',\n physicalDivider: 'PHYSICAL_DIVIDER',\n physicalDividerLessThan3m: 'PHYSICAL_DIVIDER_LESS_THAN_3M',\n physicalDividerGuardrail: 'PHYSICAL_DIVIDER_GUARDRAIL',\n curb: 'CURB',\n};\n\nconst EMPTY_ROAD_INFO: RoadInformation = { properties: [] };\n\n// V3 phonetic is an object keyed by alphabet ({lhp}|{ipa}); the SDK exposes a single flat string.\nconst toTextWithPhonetics = (t: TextWithPhoneticsAPI): TextWithPhonetics => {\n const phonetic = t.phonetic?.lhp ?? t.phonetic?.ipa;\n return {\n text: t.text,\n ...(phonetic && { phonetic }),\n ...(t.phoneticLanguageCode && { phoneticLanguageCode: t.phoneticLanguageCode }),\n };\n};\n\nconst parseInstructionRoadInformation = (road: InstructionRoadInformationAPI): RoadInformation => ({\n properties: (road.properties ?? []).map(\n (p: string) => V3_ROAD_PROPERTY_MAP[p] ?? (p.toUpperCase() as RoadInformationProperty),\n ),\n ...(road.roadNames?.length && { streetName: toTextWithPhonetics(road.roadNames[0].identifier) }),\n ...(road.roadShields?.length && {\n roadShields: road.roadShields.map(\n (s: InstructionRoadShieldAPI): RoadShield => ({\n roadNumber: s.roadNumber ? toTextWithPhonetics(s.roadNumber) : { text: '' },\n countryCode: toIso3(s.countryCodeIso2 ?? ''),\n ...(s.countrySubdivisionCodeIso && { stateCode: s.countrySubdivisionCodeIso }),\n ...(s.iconReference && { roadShieldReference: s.iconReference }),\n }),\n ),\n }),\n});\n\nconst parseSignpost = (signpost: NonNullable<InstructionAPI['signpost']>): Signpost | undefined => {\n if (!signpost.exitName && !signpost.exitNumber && !signpost.towardName) return undefined;\n return {\n ...(signpost.exitName && { exitName: toTextWithPhonetics(signpost.exitName) }),\n ...(signpost.exitNumber && { exitNumber: toTextWithPhonetics(signpost.exitNumber) }),\n // towardName is required on the SDK type; default to empty text when the API omits it.\n towardName: signpost.towardName ? toTextWithPhonetics(signpost.towardName) : { text: '' },\n };\n};\n\n// Tollgate + border-crossing fields, only present on the relevant maneuvers (kept separate to keep\n// the main instruction builder's complexity in check).\nconst parseTollAndCrossing = (a: InstructionAPI): Partial<Instruction> => ({\n ...(a.tollgateName && { tollgateName: toTextWithPhonetics(a.tollgateName) }),\n ...(a.tollPaymentTypes?.length && {\n tollPaymentTypes: a.tollPaymentTypes.map((t) => V3_TOLL_PAYMENT_MAP[t] ?? (t as TollPaymentType)),\n }),\n ...(a.countryCrossingFromName && { countryCrossingFromName: toTextWithPhonetics(a.countryCrossingFromName) }),\n ...(a.countryCrossingFromCodeIso2 && { countryCrossingFromCode: toIso3(a.countryCrossingFromCodeIso2) }),\n ...(a.countryCrossingToName && { countryCrossingToName: toTextWithPhonetics(a.countryCrossingToName) }),\n ...(a.countryCrossingToCodeIso2 && { countryCrossingToCode: toIso3(a.countryCrossingToCodeIso2) }),\n});\n\n// Optional maneuver detail fields, only present on the relevant maneuver kinds.\nconst parseManeuverExtras = (a: InstructionAPI): Partial<Instruction> => ({\n ...(a.intersectionName && { intersectionName: toTextWithPhonetics(a.intersectionName) }),\n ...(a.drivingSide && { drivingSide: a.drivingSide.toUpperCase() as DrivingSide }),\n ...(a.landmark && { landmark: V3_LANDMARK_MAP[a.landmark] ?? (a.landmark as Landmark) }),\n ...(!isNil(a.distanceToPreviousTrafficLightInMeters) && {\n trafficLightOffsetInMeters: a.distanceToPreviousTrafficLightInMeters,\n }),\n ...(a.sideRoads && { sideRoads: a.sideRoads }),\n ...(!isNil(a.isManeuverObligatory) && { isManeuverObligatory: a.isManeuverObligatory }),\n ...(!isNil(a.changeOfAngleInDegrees) && { changeOfAngleInDegrees: a.changeOfAngleInDegrees }),\n ...(!isNil(a.ambiguousExitOffsetFromManeuverInMeters) && {\n offsetOfAmbiguousExitFromManeuverInMeters: a.ambiguousExitOffsetFromManeuverInMeters,\n }),\n ...(!isNil(a.roundaboutExitNumber) && { roundaboutExitNumber: a.roundaboutExitNumber }),\n});\n\n// Road-shield related fields: signpost, the atlas base URL, and the flat reference lists. V2 exposed\n// a flat per-instruction road-shield list; V3 carries them under nextRoadInformation.\nconst parseRoadShieldFields = (a: InstructionAPI, roadShieldAtlasReference?: string): Partial<Instruction> => {\n const signpost = a.signpost ? parseSignpost(a.signpost) : undefined;\n const roadShieldReferences = (a.nextRoadInformation?.roadShields ?? [])\n .map((s) => s.iconReference)\n .filter((ref): ref is RoadShieldReference => !!ref);\n return {\n ...(signpost && { signpost }),\n ...(roadShieldAtlasReference && { roadShieldAtlasReference }),\n ...(roadShieldReferences.length && { roadShieldReferences }),\n ...(a.signpost?.exitIconReference && {\n signpostRoadShieldReferences: [a.signpost.exitIconReference],\n }),\n };\n};\n\nconst parseInstruction = (\n a: InstructionAPI,\n maneuverPoint: [number, number],\n pathPointIndex: number,\n roadShieldAtlasReference?: string,\n): Instruction => ({\n maneuverPoint,\n pathPointIndex,\n routeOffsetInMeters: a.routeOffsetInMeters,\n maneuver: (a.maneuver ? (V3_MANEUVER_MAP[a.maneuver] ?? a.maneuver) : a.maneuver) as Maneuver,\n routePath: (a.routePath ?? []).map((rp) => ({\n point: [rp.point.longitude, rp.point.latitude] as [number, number],\n distanceInMeters: rp.distanceFromRouteStartInMeters,\n travelTimeInSeconds: rp.travelTimeFromRouteStartInSeconds,\n })),\n nextRoadInfo: a.nextRoadInformation ? parseInstructionRoadInformation(a.nextRoadInformation) : EMPTY_ROAD_INFO,\n previousRoadInfo: a.previousRoadInformation\n ? parseInstructionRoadInformation(a.previousRoadInformation)\n : EMPTY_ROAD_INFO,\n ...parseManeuverExtras(a),\n ...parseRoadShieldFields(a, roadShieldAtlasReference),\n ...parseTollAndCrossing(a),\n});\n\nconst parseGuidance = (\n instructions: InstructionAPI[],\n path: Position[],\n roadShieldAtlasReference?: string,\n): Guidance => {\n const parsed: Instruction[] = [];\n let lastPathIndex = 0;\n\n for (const apiInstruction of instructions) {\n // V3 uses {latitude, longitude}, convert to GeoJSON [longitude, latitude] Position.\n const maneuverPoint: [number, number] = [\n apiInstruction.maneuverPoint.longitude,\n apiInstruction.maneuverPoint.latitude,\n ];\n\n for (let pathIndex = lastPathIndex; pathIndex < path.length; pathIndex++) {\n if (similar(path[pathIndex], maneuverPoint)) {\n lastPathIndex = pathIndex;\n break;\n }\n }\n\n parsed.push(parseInstruction(apiInstruction, maneuverPoint, lastPathIndex, roadShieldAtlasReference));\n }\n\n return { instructions: parsed };\n};\n\nconst parseRoute = (\n apiRoute: RouteAPI,\n index: number,\n params: CalculateRouteParams,\n roadShieldAtlasReference?: string,\n): Route => {\n const geometry = parseRoutePath(apiRoute.legs);\n return {\n type: 'Feature',\n geometry,\n id: generateId(),\n bbox: bboxFromGeoJSON(geometry) as BBox,\n properties: {\n index,\n summary: parseSummary(apiRoute.summary, params),\n sections: parseSections(apiRoute, params),\n ...(apiRoute.instructions?.length && {\n guidance: parseGuidance(apiRoute.instructions, geometry.coordinates, roadShieldAtlasReference),\n }),\n ...(apiRoute.progressPoints && {\n progress: apiRoute.progressPoints.map((progressPoint: ProgressPointAPI) => ({\n pointIndex: progressPoint.pathIndex,\n distanceInMeters: progressPoint.distanceInMeters,\n travelTimeInSeconds: progressPoint.travelDurationInSeconds,\n })),\n }),\n },\n };\n};\n\n/**\n * Default method for parsing calculate route response from {@link CalculateRouteResponseAPI}\n * @param apiResponse The Routing API response.\n * @param params The params used to calculate this route.\n */\nexport const parseCalculateRouteResponse = (\n apiResponse: CalculateRouteResponseAPI,\n params: CalculateRouteParams,\n): Routes => {\n const features = apiResponse.routes.map((apiRoute, index) =>\n parseRoute(apiRoute, index, params, apiResponse.roadShieldAtlasReference),\n );\n const bbox = bboxFromGeoJSON(features);\n return { type: 'FeatureCollection', ...(bbox && { bbox }), features };\n};\n","import type { Routes } from '@tomtom-org/maps-sdk/core';\nimport type { FetchInput, ServiceTemplate } from '../shared';\nimport { fetchWith } from '../shared/fetch';\nimport { routeRequestValidationConfig } from './calculateRouteRequestSchema';\nimport { buildCalculateRouteRequest } from './requestBuilder';\nimport { parseCalculateRouteResponse } from './responseParser';\nimport { parseRoutingResponseError } from './routingResponseErrorParser';\nimport type { CalculateRoutePOSTDataAPI } from './types/apiRequestTypes';\nimport type { CalculateRouteResponseAPI } from './types/apiResponseTypes';\nimport type { CalculateRouteParams } from './types/calculateRouteParams';\n\n/**\n * @ignore\n */\nexport type CalculateRouteTemplate = ServiceTemplate<\n CalculateRouteParams,\n FetchInput<CalculateRoutePOSTDataAPI>,\n CalculateRouteResponseAPI,\n Routes\n>;\n\n/**\n * @ignore\n */\nexport const calculateRouteTemplate: CalculateRouteTemplate = {\n requestValidation: routeRequestValidationConfig,\n buildRequest: buildCalculateRouteRequest,\n sendRequest: fetchWith,\n parseResponse: parseCalculateRouteResponse,\n parseResponseError: parseRoutingResponseError,\n getAPIVersion: () => 3,\n};\n","import type { CalculateRouteTemplate } from './calculateRouteTemplate';\nimport { calculateRouteTemplate } from './calculateRouteTemplate';\nimport { buildCalculateRouteRequest } from './requestBuilder';\nimport { parseCalculateRouteResponse } from './responseParser';\n\nconst customize: {\n buildCalculateRouteRequest: typeof buildCalculateRouteRequest;\n parseCalculateRouteResponse: typeof parseCalculateRouteResponse;\n calculateRouteTemplate: CalculateRouteTemplate;\n} = {\n buildCalculateRouteRequest,\n parseCalculateRouteResponse,\n calculateRouteTemplate,\n};\nexport default customize;\n","import type { AreaAnalyticsMetricKey } from '@tomtom-org/maps-sdk/core';\nimport type { MultiPolygon, Polygon } from 'geojson';\nimport type { CommonServiceParams } from '../../shared';\n\n// ---------------------------------------------------------------------------\n// Functional Road Classes\n// ---------------------------------------------------------------------------\n\n/**\n * Ordered list of Functional Road Class identifiers, from highest importance (motorways) to lowest.\n *\n * @remarks\n * The index of each class in this array corresponds to its FRC value in the API (0 for 'MOTORWAY', 1 for 'MAJOR_ROAD', ..., 8 for 'OTHER_ROAD').\n *\n * @see https://docs.tomtom.com/traffic-stats/documentation/product-information/faq#what-are-functional-road-classes-frc\n *\n * @group Traffic\n */\nexport const functionalRoadClasses = [\n 'MOTORWAY',\n 'MAJOR_ROAD',\n 'OTHER_MAJOR_ROAD',\n 'SECONDARY_ROAD',\n 'LOCAL_CONNECTING_ROAD',\n 'LOCAL_ROAD_HIGH_IMPORTANCE',\n 'LOCAL_ROAD',\n 'LOCAL_ROAD_MINOR_IMPORTANCE',\n 'OTHER_ROAD',\n] as const;\n\n/**\n * A Functional Road Class (FRC) identifier — classifies road segments by their importance\n * in the road network.\n *\n * | Value | Description |\n * |---|---|\n * | `'MOTORWAY'` | Motorways, freeways, and major roads |\n * | `'MAJOR_ROAD'` | Major roads, less important than motorways |\n * | `'OTHER_MAJOR_ROAD'` | Other major roads connecting neighbouring regions |\n * | `'SECONDARY_ROAD'` | Secondary roads linking parts of the same region |\n * | `'LOCAL_CONNECTING_ROAD'` | Local roads providing settlement access |\n * | `'LOCAL_ROAD_HIGH_IMPORTANCE'` | Local roads of high importance within a settlement |\n * | `'LOCAL_ROAD'` | Local roads within settlement sections |\n * | `'LOCAL_ROAD_MINOR_IMPORTANCE'` | Local roads of minor importance (dead-ends, alleys) |\n * | `'OTHER_ROAD'` | Other roads (paths, cycle routes, pedestrian infrastructure) |\n *\n * @group Traffic\n */\nexport type FunctionalRoadClass = (typeof functionalRoadClasses)[number];\n\n// ---------------------------------------------------------------------------\n// Metric keys\n// ---------------------------------------------------------------------------\n\n/**\n * Maps a camelCase {@link AreaAnalyticsMetricKey} to the UPPER_CASE string expected by the API.\n *\n * @internal\n */\nexport const metricKeyToApiDataType: Record<AreaAnalyticsMetricKey, string> = {\n speed: 'SPEED',\n freeFlowSpeed: 'FREE_FLOW_SPEED',\n congestionLevel: 'CONGESTION_LEVEL',\n travelTime: 'TRAVEL_TIME',\n networkLength: 'NETWORK_LENGTH',\n};\n\n// ---------------------------------------------------------------------------\n// Date input\n// ---------------------------------------------------------------------------\n\n/**\n * A date value accepted by Traffic Area Analytics — either a `Date` object or\n * an ISO `'YYYY-MM-DD'` string.\n * `Date` objects are automatically converted to `YYYY-MM-DD` format before\n * being sent to the API.\n *\n * @group Traffic\n */\nexport type AreaAnalyticsDateInput = string | Date;\n\n// ---------------------------------------------------------------------------\n// Date selection — exactly one variant must be supplied\n// ---------------------------------------------------------------------------\n\n/**\n * Continuous date-range variant: analyse traffic between `startDate` and `endDate`.\n *\n * - `endDate` is optional; when omitted it defaults to **today**.\n * - The range must not exceed **31 days**.\n * - Mutually exclusive with {@link TrafficAreaAnalyticsDays}.\n *\n * @group Traffic\n */\nexport type TrafficAreaAnalyticsDateRange = {\n /**\n * Start date of the analysis period.\n * Accepts a `Date` object or an ISO `'YYYY-MM-DD'` string.\n *\n * @example '2024-08-01'\n * @example new Date('2024-08-01')\n */\n startDate: AreaAnalyticsDateInput;\n\n /**\n * End date of the analysis period (inclusive).\n * Accepts a `Date` object or an ISO `'YYYY-MM-DD'` string.\n * When omitted, defaults to **today**.\n *\n * @remarks Must be within 31 days from `startDate`.\n *\n * @example '2024-08-07'\n * @example new Date('2024-08-07')\n */\n endDate?: AreaAnalyticsDateInput;\n\n days?: never;\n};\n\n/**\n * Specific-dates variant: analyse traffic on an explicit list of dates.\n *\n * Useful for non-consecutive dates such as \"all Mondays in August\".\n * Mutually exclusive with {@link TrafficAreaAnalyticsDateRange}.\n *\n * @group Traffic\n */\nexport type TrafficAreaAnalyticsDays = {\n /**\n * List of specific dates to include in the analysis.\n * Accepts `Date` objects or ISO `'YYYY-MM-DD'` strings.\n *\n * @example ['2024-08-05', '2024-08-12', '2024-08-19', '2024-08-26']\n */\n days: AreaAnalyticsDateInput[];\n\n startDate?: never;\n endDate?: never;\n};\n\n// ---------------------------------------------------------------------------\n// Main params type\n// ---------------------------------------------------------------------------\n\n/**\n * Parameters for the Traffic Area Analytics Lite service.\n *\n * @remarks\n * Supply **either** `startDate` (+ optional `endDate`) **or** `days` — these\n * two options are mutually exclusive.\n *\n * The lite endpoint runs analysis synchronously and returns results immediately,\n * with the constraint that the date range must not exceed 31 days and only a\n * single region may be submitted.\n *\n * @example\n * ```typescript\n * // Continuous range — endDate defaults to today when omitted\n * const result = await trafficAreaAnalytics({\n * startDate: '2024-08-01',\n * functionalRoadClasses: ['MOTORWAY', 'MAJOR_ROAD', 'OTHER_MAJOR_ROAD', 'SECONDARY_ROAD', 'LOCAL_CONNECTING_ROAD', 'LOCAL_ROAD_HIGH_IMPORTANCE'],\n * hours: [7, 8, 9, 17, 18],\n * metrics: ['speed', 'congestionLevel'],\n * geometry: {\n * type: 'Polygon',\n * coordinates: [[[4.896128, 52.382402], [4.875701, 52.368459], [4.923611, 52.36341], [4.896128, 52.382402]]]\n * }\n * });\n * ```\n *\n * @example\n * ```typescript\n * // Specific dates\n * const result = await trafficAreaAnalytics({\n * days: ['2024-08-05', '2024-08-12', '2024-08-19', '2024-08-26'],\n * functionalRoadClasses: ['MOTORWAY', 'MAJOR_ROAD', 'OTHER_MAJOR_ROAD'],\n * hours: [8, 9, 17, 18],\n * metrics: ['speed'],\n * geometry: polygon\n * });\n * ```\n *\n * @group Traffic\n */\nexport type TrafficAreaAnalyticsParams = CommonServiceParams &\n TrafficAreaAnalyticsBaseParams &\n (TrafficAreaAnalyticsDateRange | TrafficAreaAnalyticsDays);\n\n/**\n * @ignore\n */\ntype TrafficAreaAnalyticsBaseParams = {\n /**\n * Optional name for the analysis report.\n */\n name?: string;\n\n /**\n * Traffic metrics to include in the analysis, or `'all'` to include every metric.\n *\n * @remarks\n * At least one metric must be provided, or pass `'all'` as a shorthand for all five metrics.\n *\n * @example ['speed', 'congestionLevel', 'freeFlowSpeed']\n * @example 'all'\n */\n metrics: AreaAnalyticsMetricKey[] | 'all';\n\n /**\n * Functional road classes to include in the analysis, or `'all'` to include every class.\n *\n * @remarks\n * At least one value must be provided, or pass `'all'` as a shorthand for all nine classes.\n *\n * @example ['MOTORWAY', 'MAJOR_ROAD', 'OTHER_MAJOR_ROAD']\n * @example 'all'\n */\n functionalRoadClasses: FunctionalRoadClass[] | 'all';\n\n /**\n * Hours of the day to include in the analysis (0–23), or `'all'` to include every hour.\n *\n * @remarks\n * Provide at least one hour, or pass `'all'` as a shorthand for `[0, 1, 2, ..., 23]`.\n *\n * @example [7, 8, 9, 17, 18]\n * @example 'all'\n */\n hours: number[] | 'all';\n\n /**\n * The GeoJSON Polygon or MultiPolygon geometry defining the analysis region.\n *\n * @remarks\n * The lite endpoint accepts exactly one region. For multi-region analysis,\n * use the standard (async) report creation endpoint.\n */\n geometry: Polygon | MultiPolygon;\n};\n","import { areaAnalyticsMetricKeys } from '@tomtom-org/maps-sdk/core';\nimport type { FetchInput } from '../shared';\nimport { appendCommonParams } from '../shared/request/requestBuildingUtils';\nimport type { AreaAnalyticsRequestBody } from './types/apiTypes';\nimport type {\n AreaAnalyticsDateInput,\n FunctionalRoadClass,\n TrafficAreaAnalyticsParams,\n} from './types/trafficAreaAnalyticsParams';\nimport { functionalRoadClasses, metricKeyToApiDataType } from './types/trafficAreaAnalyticsParams';\n\nconst AREA_ANALYTICS_URL_PATH = '/areaanalytics/reports/lite';\n\nconst buildUrlBasePath = (params: TrafficAreaAnalyticsParams): string =>\n params.customServiceBaseURL ?? `${params.commonBaseURL}${AREA_ANALYTICS_URL_PATH}`;\n\nconst toDateString = (date: AreaAnalyticsDateInput): string =>\n // (ensure to get only YYYY-MM-DD portion of the dates)\n date instanceof Date ? date.toISOString().slice(0, 10) : date;\n\nconst ALL_HOURS = Array.from({ length: 24 }, (_, i) => i);\n\nconst frcToNumber = (frc: FunctionalRoadClass): number => functionalRoadClasses.indexOf(frc);\n\n/**\n * Default method for building a Traffic Area Analytics request from {@link TrafficAreaAnalyticsParams}.\n * @param params The traffic area analytics parameters, with global configuration already merged.\n */\nexport const buildTrafficAreaAnalyticsRequest = (\n params: TrafficAreaAnalyticsParams,\n): FetchInput<AreaAnalyticsRequestBody> => {\n const url = new URL(buildUrlBasePath(params));\n appendCommonParams(url.searchParams, params);\n\n const daysAgo = (numDays: number): Date => {\n const date = new Date();\n date.setDate(date.getDate() - numDays);\n return date;\n };\n\n const datePart = Array.isArray(params.days)\n ? { days: params.days.map(toDateString) }\n : {\n startDate: toDateString(params.startDate ?? daysAgo(3)),\n endDate: toDateString(params.endDate ?? daysAgo(2)),\n };\n\n const body: AreaAnalyticsRequestBody = {\n name: params.name ?? `Maps SDK JS Area Analytics Report ${new Date().toISOString()}`,\n ...datePart,\n dataTypes:\n params.metrics === 'all'\n ? areaAnalyticsMetricKeys.map((k) => metricKeyToApiDataType[k])\n : params.metrics.map((k) => metricKeyToApiDataType[k]),\n frcs:\n params.functionalRoadClasses === 'all'\n ? functionalRoadClasses.map((_, i) => i)\n : params.functionalRoadClasses.map(frcToNumber),\n hours: params.hours === 'all' ? ALL_HOURS : params.hours,\n features: [{ type: 'Feature', geometry: params.geometry }],\n };\n\n return { method: 'POST', url, data: body };\n};\n","import {\n AreaAnalyticsAnomaly,\n AreaAnalyticsFeature,\n AreaAnalyticsFeatureProperties,\n AreaAnalyticsMetricKey,\n AreaAnalyticsMetricRange,\n AreaAnalyticsMetrics,\n AreaAnalyticsTileEntry,\n AreaAnalyticsTimedData,\n AreaAnalyticsTimedEntry,\n areaAnalyticsMetricKeys,\n BBox,\n bboxFromGeoJSON,\n TrafficAreaAnalytics,\n} from '@tomtom-org/maps-sdk/core';\nimport type {\n AnomalyAPI,\n AreaAnalyticsFeatureAPI,\n AreaAnalyticsResponseAPI,\n FeaturePropertiesAPI,\n MetricsAPI,\n TiledEntryAPI,\n TimedEntryAPI,\n} from './types/apiTypes';\n\nconst parseMetrics = (api: MetricsAPI): AreaAnalyticsMetrics => ({\n ...(api.v !== undefined && { speed: api.v }),\n ...(api.fv !== undefined && { freeFlowSpeed: api.fv }),\n ...(api.c !== undefined && { congestionLevel: api.c }),\n ...(api.t !== undefined && { travelTime: api.t }),\n ...(api.l !== undefined && { networkLength: api.l }),\n});\n\nconst parseTimedEntry = (entry: TimedEntryAPI): AreaAnalyticsTimedEntry => ({\n ...(entry.date !== undefined && { date: new Date(entry.date) }),\n ...(entry.year !== undefined && { year: entry.year }),\n ...(entry.month !== undefined && { month: entry.month }),\n ...(entry.week !== undefined && { week: entry.week }),\n ...(entry.hour !== undefined && { hour: entry.hour }),\n ...(entry.day !== undefined && { day: entry.day }),\n ...parseMetrics(entry),\n});\n\n/**\n * Adds a given number of days to a Date, returning a new Date (no mutation).\n */\nconst addDays = (base: Date, days: number): Date => {\n const d = new Date(base);\n d.setDate(d.getDate() + days);\n return d;\n};\n\n/**\n * Parses hourly entries (sequential, 24 per day) and computes `date` + `hour`\n * from the sequential index when the API doesn't provide them.\n */\nconst parseHourlyEntries = (entries: TimedEntryAPI[], startDate: Date): AreaAnalyticsTimedEntry[] =>\n entries.map((entry, index) => {\n const base = parseTimedEntry(entry);\n // Only compute if the API didn't already provide the fields\n if (base.date === undefined) {\n base.date = addDays(startDate, Math.floor(index / 24));\n }\n if (base.hour === undefined) {\n base.hour = index % 24;\n }\n return base;\n });\n\n/**\n * Parses daily entries (sequential, one per day) and computes `date`\n * from the sequential index when the API doesn't provide it.\n */\nconst parseDailyEntries = (entries: TimedEntryAPI[], startDate: Date): AreaAnalyticsTimedEntry[] =>\n entries.map((entry, index) => {\n const base = parseTimedEntry(entry);\n if (base.date === undefined) {\n base.date = addDays(startDate, index);\n }\n return base;\n });\n\n/**\n * Parses average entries (sequential, 24 per day-of-week, 7 days) and computes\n * `day` (1=Monday…7=Sunday) + `hour` from the sequential index when the API\n * doesn't provide them.\n */\nconst parseAverageEntries = (entries: TimedEntryAPI[]): AreaAnalyticsTimedEntry[] =>\n entries.map((entry, index) => {\n const base = parseTimedEntry(entry);\n if (base.day === undefined) {\n base.day = Math.floor(index / 24) + 1; // 1 = Monday … 7 = Sunday\n }\n if (base.hour === undefined) {\n base.hour = index % 24;\n }\n return base;\n });\n\nconst parseTileEntry = (tile: TiledEntryAPI): AreaAnalyticsTileEntry => ({\n tileCentre: [tile.lon, tile.lat],\n ...parseMetrics(tile),\n});\n\nconst parseAnomaly = (api: AnomalyAPI): AreaAnalyticsAnomaly => ({\n startDate: new Date(api.startDate),\n endDate: new Date(api.endDate),\n labels: api.labels,\n});\n\nconst parseTimedData = (api: FeaturePropertiesAPI['timedData'], startDate: Date): AreaAnalyticsTimedData => ({\n ...(api.yearly && { yearly: api.yearly.map(parseTimedEntry) }),\n ...(api.monthly && { monthly: api.monthly.map(parseTimedEntry) }),\n ...(api.weekly && { weekly: api.weekly.map(parseTimedEntry) }),\n ...(api.daily && { daily: parseDailyEntries(api.daily, startDate) }),\n ...(api.hourly && { hourly: parseHourlyEntries(api.hourly, startDate) }),\n ...(api.average && { average: parseAverageEntries(api.average) }),\n});\n\nconst API_DATA_TYPE_TO_METRIC_KEY: Record<string, AreaAnalyticsMetricKey> = {\n SPEED: 'speed',\n FREE_FLOW_SPEED: 'freeFlowSpeed',\n CONGESTION_LEVEL: 'congestionLevel',\n TRAVEL_TIME: 'travelTime',\n NETWORK_LENGTH: 'networkLength',\n};\n\nconst parseAnomalies = (\n api: Record<string, AnomalyAPI[]>,\n): Partial<Record<AreaAnalyticsMetricKey, AreaAnalyticsAnomaly[]>> => {\n const result: Partial<Record<AreaAnalyticsMetricKey, AreaAnalyticsAnomaly[]>> = {};\n for (const [key, anomalies] of Object.entries(api)) {\n const metricKey = API_DATA_TYPE_TO_METRIC_KEY[key];\n if (metricKey) result[metricKey] = anomalies.map(parseAnomaly);\n }\n return result;\n};\n\nconst parseFeatureProperties = (api: FeaturePropertiesAPI, startDate: Date): AreaAnalyticsFeatureProperties => ({\n name: api.name,\n timezone: api.timezone,\n level: api.level,\n baseData: parseMetrics(api.baseData),\n timedData: parseTimedData(api.timedData, startDate),\n ...(api.tiledData && {\n tiledData: { tiles: api.tiledData.tiles.map(parseTileEntry) },\n }),\n ...(api.anomalies && {\n anomalies: parseAnomalies(api.anomalies),\n }),\n});\n\nconst parseFeature = (apiFeature: AreaAnalyticsFeatureAPI, startDate: Date): AreaAnalyticsFeature => ({\n type: apiFeature.type,\n id: apiFeature.id,\n geometry: apiFeature.geometry,\n properties: parseFeatureProperties(apiFeature.properties, startDate),\n bbox: bboxFromGeoJSON(apiFeature.geometry) as BBox,\n});\n\nconst computeTileRanges = (\n features: AreaAnalyticsFeature[],\n): Partial<Record<AreaAnalyticsMetricKey, AreaAnalyticsMetricRange>> => {\n const tiles = features.flatMap((f) => f.properties.tiledData?.tiles ?? []);\n const ranges: Partial<Record<AreaAnalyticsMetricKey, AreaAnalyticsMetricRange>> = {};\n for (const key of areaAnalyticsMetricKeys) {\n const values = tiles.map((t) => t[key]).filter((v): v is number => v != null);\n if (values.length > 0) {\n ranges[key] = { min: Math.min(...values), max: Math.max(...values) };\n }\n }\n return ranges;\n};\n\n/**\n * Parses a raw Traffic Area Analytics API response into the SDK representation.\n *\n * In addition to renaming abbreviated metric fields and converting date strings\n * to `Date` objects, this function computes data-driven min/max ranges from\n * all tile entries across every feature and attaches them to\n * `result.properties.ranges`.\n *\n * @param apiResponse - The raw Area Analytics API response.\n */\nexport const parseTrafficAreaAnalyticsResponse = (apiResponse: AreaAnalyticsResponseAPI): TrafficAreaAnalytics => {\n const startDate = new Date(apiResponse.properties.startDate);\n const features = apiResponse.features.map((f) => parseFeature(f, startDate));\n\n return {\n type: 'FeatureCollection',\n properties: {\n startDate,\n endDate: new Date(apiResponse.properties.endDate),\n metrics: apiResponse.properties.dataTypes\n .map((dt) => API_DATA_TYPE_TO_METRIC_KEY[dt])\n .filter((key): key is AreaAnalyticsMetricKey => key !== undefined),\n heatmap: apiResponse.properties.heatmap,\n frcs: apiResponse.properties.frcs,\n ranges: computeTileRanges(features),\n },\n features,\n };\n};\n","import { areaAnalyticsMetricKeys } from '@tomtom-org/maps-sdk/core';\nimport { z } from 'zod';\nimport { commonServiceRequestSchema } from '../shared/schema/commonParamsSchema';\nimport { functionalRoadClasses } from './types/trafficAreaAnalyticsParams';\n\nconst dateInputSchema = z.union([\n z.date(),\n z.string().regex(/^\\d{4}-\\d{2}-\\d{2}$/, \"Date must be in 'YYYY-MM-DD' format\"),\n]);\n\nconst geometrySchema = z.union([\n z.object({\n type: z.literal('Polygon'),\n coordinates: z.array(z.array(z.array(z.number()))).min(1),\n }),\n z.object({\n type: z.literal('MultiPolygon'),\n coordinates: z.array(z.array(z.array(z.array(z.number())))).min(1),\n }),\n]);\n\nconst toDateObj = (d: Date | string): Date => (d instanceof Date ? d : new Date(d));\n\nconst startOfDayUTC = (d: Date | string): number => {\n const date = toDateObj(d);\n return Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate());\n};\n\nconst ONE_DAY_MS = 24 * 60 * 60 * 1000;\n\nconst isAtLeastTwoDaysBeforeToday = (d: Date | string): boolean => {\n const now = new Date();\n const todayUTC = Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate());\n return startOfDayUTC(d) <= todayUTC - 2 * ONE_DAY_MS;\n};\n\n/**\n * @ignore\n */\nexport const trafficAreaAnalyticsRequestSchema = commonServiceRequestSchema\n .extend({\n name: z.string().optional(),\n startDate: dateInputSchema.optional(),\n endDate: dateInputSchema.optional(),\n days: z.array(dateInputSchema).min(1).optional(),\n metrics: z.union([z.array(z.enum(areaAnalyticsMetricKeys)).min(1), z.literal('all')]),\n functionalRoadClasses: z.union([z.array(z.enum(functionalRoadClasses)).min(1), z.literal('all')]),\n hours: z.union([z.array(z.number().int().min(0).max(23)).min(1), z.literal('all')]),\n geometry: geometrySchema,\n })\n .refine(\n (data) => {\n const hasDays = data.days !== undefined;\n const hasStart = data.startDate !== undefined;\n\n // Exactly one mode: either days OR startDate (+optional endDate)\n if (hasDays && hasStart) return false;\n if (!hasDays && !hasStart) return false;\n\n // Validate range for continuous range: startDate must be at least 1 day before endDate, within 31 days\n if (data.startDate !== undefined) {\n const effectiveEnd = data.endDate !== undefined ? toDateObj(data.endDate) : new Date();\n const diffDays = (effectiveEnd.getTime() - toDateObj(data.startDate).getTime()) / (1000 * 60 * 60 * 24);\n return diffDays >= 1 && diffDays <= 31;\n }\n\n return true;\n },\n {\n message:\n 'Provide either startDate (with optional endDate, at least 1 day apart and within 31 days) or days — but not both',\n },\n )\n .refine(\n (data) => {\n if (data.days !== undefined) {\n return data.days.every(isAtLeastTwoDaysBeforeToday);\n }\n\n const effectiveEnd = data.endDate ?? new Date();\n return isAtLeastTwoDaysBeforeToday(effectiveEnd);\n },\n {\n message: 'Dates must be at least 2 days before today',\n },\n );\n","import type { TrafficAreaAnalytics } from '@tomtom-org/maps-sdk/core';\nimport type { FetchInput, ServiceTemplate } from '../shared';\nimport { fetchWith } from '../shared/fetch';\nimport { buildTrafficAreaAnalyticsRequest } from './requestBuilder';\nimport { parseTrafficAreaAnalyticsResponse } from './responseParser';\nimport { trafficAreaAnalyticsRequestSchema } from './trafficAreaAnalyticsRequestSchema';\nimport type { AreaAnalyticsRequestBody, AreaAnalyticsResponseAPI } from './types/apiTypes';\nimport type { TrafficAreaAnalyticsParams } from './types/trafficAreaAnalyticsParams';\n\n/**\n * Traffic Area Analytics service template type.\n * @ignore\n */\nexport type TrafficAreaAnalyticsTemplate = ServiceTemplate<\n TrafficAreaAnalyticsParams,\n FetchInput<AreaAnalyticsRequestBody>,\n AreaAnalyticsResponseAPI,\n TrafficAreaAnalytics\n>;\n\n/**\n * Traffic Area Analytics service template main implementation.\n * @ignore\n */\nexport const trafficAreaAnalyticsTemplate: TrafficAreaAnalyticsTemplate = {\n requestValidation: { schema: trafficAreaAnalyticsRequestSchema },\n buildRequest: buildTrafficAreaAnalyticsRequest,\n sendRequest: fetchWith,\n parseResponse: parseTrafficAreaAnalyticsResponse,\n};\n","import { buildTrafficAreaAnalyticsRequest } from './requestBuilder';\nimport { parseTrafficAreaAnalyticsResponse } from './responseParser';\nimport type { TrafficAreaAnalyticsTemplate } from './trafficAreaAnalyticsTemplate';\nimport { trafficAreaAnalyticsTemplate } from './trafficAreaAnalyticsTemplate';\n\nconst customize: {\n buildTrafficAreaAnalyticsRequest: typeof buildTrafficAreaAnalyticsRequest;\n parseTrafficAreaAnalyticsResponse: typeof parseTrafficAreaAnalyticsResponse;\n trafficAreaAnalyticsTemplate: TrafficAreaAnalyticsTemplate;\n} = {\n buildTrafficAreaAnalyticsRequest,\n parseTrafficAreaAnalyticsResponse,\n trafficAreaAnalyticsTemplate,\n};\nexport default customize;\n","import { bboxFromGeoJSON, trafficIncidentToIconCategory } from '@tomtom-org/maps-sdk/core';\nimport type { FetchInput } from '../shared';\nimport {\n appendByJoiningParamValue,\n appendCommonParams,\n appendOptionalParam,\n} from '../shared/request/requestBuildingUtils';\nimport type { TrafficIncidentDetailsParams } from './types/trafficIncidentDetailsParams';\n\nconst TRAFFIC_URL_PATH = '/maps/orbis/traffic';\n\n// Request all available incident fields by default so callers get a complete response without\n// having to manually specify a projection string.\nconst DEFAULT_FIELDS =\n '{incidents{type,geometry{type,coordinates},properties{id,iconCategory,magnitudeOfDelay,events{description,code,iconCategory},startTime,endTime,from,to,length,delay,roadNumbers,timeValidity,probabilityOfOccurrence,numberOfReports,lastReportTime,tmc{countryCode,tableNumber,tableVersion,direction,points{location,offset}}}}}';\n\ntype TrafficIncidentDetailsPostBody = { ids: string[] };\n\nconst buildUrlBasePath = (params: TrafficIncidentDetailsParams): string =>\n params.customServiceBaseURL ?? `${params.commonBaseURL}${TRAFFIC_URL_PATH}/incidentDetails`;\n\nconst appendOptionalParams = (urlParams: URLSearchParams, params: TrafficIncidentDetailsParams): void => {\n appendOptionalParam(urlParams, 'fields', DEFAULT_FIELDS);\n appendOptionalParam(urlParams, 't', params.trafficModelId);\n appendByJoiningParamValue(urlParams, 'categoryFilter', params.categoryFilter?.map(trafficIncidentToIconCategory));\n appendByJoiningParamValue(urlParams, 'timeValidityFilter', params.timeValidityFilter);\n};\n\n/**\n * Default method for building a traffic incident details request from {@link TrafficIncidentDetailsParams}.\n * @param params The traffic incident details parameters, with global configuration already merged into them.\n */\nexport const buildTrafficIncidentDetailsRequest = (\n params: TrafficIncidentDetailsParams,\n): FetchInput<TrafficIncidentDetailsPostBody> => {\n const url = new URL(buildUrlBasePath(params));\n const urlParams = url.searchParams;\n appendCommonParams(urlParams, params);\n appendOptionalParams(urlParams, params);\n\n if ('bbox' in params && params.bbox) {\n const resolvedBBox = bboxFromGeoJSON(params.bbox);\n if (resolvedBBox) {\n urlParams.append('bbox', resolvedBBox.join(','));\n }\n return { method: 'GET', url };\n }\n\n // ids-based request — POST when there are more than 5 IDs (GET limit), GET otherwise\n const { ids } = params as { ids: string[] };\n\n if (ids.length > 5) {\n return { method: 'POST', url, data: { ids } };\n }\n\n urlParams.append('ids', ids.join(','));\n return { method: 'GET', url };\n};\n","import {\n iconToTrafficIncidentCategory,\n indexedMagnitudes,\n type TrafficIncident,\n type TrafficIncidentDetails,\n type TrafficIncidentEvent,\n type TrafficIncidentProbability,\n type TrafficIncidentTimeValidity,\n type TrafficIncidentTMC,\n} from '@tomtom-org/maps-sdk/core';\nimport type { IncidentAPI, IncidentDetailsResponseAPI, IncidentEventAPI, IncidentTMCAPI } from './types/apiTypes';\n\nconst parseEvent = (apiEvent: IncidentEventAPI): TrafficIncidentEvent => ({\n description: apiEvent.description,\n code: apiEvent.code,\n category: iconToTrafficIncidentCategory(apiEvent.iconCategory),\n});\n\nconst parseTMC = (apiTMC: IncidentTMCAPI): TrafficIncidentTMC => ({\n countryCode: apiTMC.countryCode,\n tableNumber: apiTMC.tableNumber,\n tableVersion: apiTMC.tableVersion,\n direction: apiTMC.direction as 'positive' | 'negative',\n points: (apiTMC.points ?? []).map((p) => ({\n location: p.location,\n ...(p.offset != null && { offset: p.offset }),\n })),\n});\n\nconst parseIncident = (apiIncident: IncidentAPI): TrafficIncident => {\n const p = apiIncident.properties;\n return {\n type: 'Feature',\n // Top-level id is what the SDK's event-state machinery uses to track per-feature\n // click/hover state (see `renderedRefId` in map's featureId.ts). promoteId: 'id' on the\n // source keeps rendered features pointing at the same identifier.\n id: p.id,\n geometry: apiIncident.geometry,\n properties: {\n id: p.id,\n category: iconToTrafficIncidentCategory(p.iconCategory),\n magnitudeOfDelay: indexedMagnitudes[p.magnitudeOfDelay] ?? 'unknown',\n events: (p.events ?? []).map(parseEvent),\n ...(p.startTime && { startTime: new Date(p.startTime) }),\n ...(p.endTime && { endTime: new Date(p.endTime) }),\n ...(p.from && { from: p.from }),\n ...(p.to && { to: p.to }),\n // The API can return `length` / `delay` as an explicit `null` (e.g. closures and\n // roadworks carry no delay). `!= null` drops both `null` and `undefined` so these\n // optional numeric fields are omitted rather than set to `null` — a `null` here\n // violates the `number | undefined` type and trips MapLibre's numeric paint/filter\n // expressions (\"Expected value to be of type number, but found null instead\").\n ...(p.length != null && { lengthInMeters: p.length }),\n ...(p.delay != null && { delayInSeconds: p.delay }),\n ...(p.roadNumbers && { roadNumbers: p.roadNumbers }),\n timeValidity: p.timeValidity as TrafficIncidentTimeValidity,\n ...(p.probabilityOfOccurrence && {\n probabilityOfOccurrence: p.probabilityOfOccurrence as TrafficIncidentProbability,\n }),\n ...(p.numberOfReports != null && { numberOfReports: p.numberOfReports }),\n ...(p.lastReportTime && { lastReportTime: new Date(p.lastReportTime) }),\n ...(p.tmc && { tmc: parseTMC(p.tmc) }),\n },\n };\n};\n\n/**\n * Default method for parsing a traffic incident details API response.\n * @param apiResponse The raw Traffic Incident Details API response.\n */\nexport const parseTrafficIncidentDetailsResponse = (\n apiResponse: IncidentDetailsResponseAPI,\n): TrafficIncidentDetails => ({\n type: 'FeatureCollection',\n features: (apiResponse.incidents ?? []).filter((i): i is IncidentAPI => i !== null).map(parseIncident),\n});\n","import { trafficIncidentRequestCategories } from '@tomtom-org/maps-sdk/core';\nimport { z } from 'zod';\nimport { commonServiceRequestSchema } from '../shared/schema/commonParamsSchema';\nimport { hasBBoxSchema } from '../shared/schema/geometriesSchema';\n\n/**\n * @ignore\n */\nexport const trafficIncidentDetailsRequestSchema = commonServiceRequestSchema\n .extend({\n bbox: hasBBoxSchema.optional(),\n ids: z.array(z.string()).optional(),\n trafficModelId: z.string().optional(),\n categoryFilter: z.array(z.enum(trafficIncidentRequestCategories)).optional(),\n timeValidityFilter: z.array(z.enum(['present', 'future'])).optional(),\n })\n .refine((data) => !(data.bbox && data.ids), {\n message: 'Provide either bbox or ids, not both',\n });\n","import type { TrafficIncidentDetails } from '@tomtom-org/maps-sdk/core';\nimport type { FetchInput, ServiceTemplate } from '../shared';\nimport { fetchWith } from '../shared/fetch';\nimport { buildTrafficIncidentDetailsRequest } from './requestBuilder';\nimport { parseTrafficIncidentDetailsResponse } from './responseParser';\nimport { trafficIncidentDetailsRequestSchema } from './trafficIncidentDetailsRequestSchema';\nimport type { IncidentDetailsResponseAPI } from './types/apiTypes';\nimport type { TrafficIncidentDetailsParams } from './types/trafficIncidentDetailsParams';\n\ntype TrafficIncidentDetailsPostBody = { ids: string[] };\n\n/**\n * Traffic Incident Details service template type.\n * @ignore\n */\nexport type TrafficIncidentDetailsTemplate = ServiceTemplate<\n TrafficIncidentDetailsParams,\n FetchInput<TrafficIncidentDetailsPostBody>,\n IncidentDetailsResponseAPI,\n TrafficIncidentDetails\n>;\n\n/**\n * Traffic Incident Details service template main implementation.\n * @ignore\n */\nexport const trafficIncidentDetailsTemplate: TrafficIncidentDetailsTemplate = {\n requestValidation: { schema: trafficIncidentDetailsRequestSchema },\n buildRequest: buildTrafficIncidentDetailsRequest,\n sendRequest: fetchWith,\n parseResponse: parseTrafficIncidentDetailsResponse,\n};\n","import { buildTrafficIncidentDetailsRequest } from './requestBuilder';\nimport { parseTrafficIncidentDetailsResponse } from './responseParser';\nimport type { TrafficIncidentDetailsTemplate } from './trafficIncidentDetailsTemplate';\nimport { trafficIncidentDetailsTemplate } from './trafficIncidentDetailsTemplate';\n\nconst customize: {\n buildTrafficIncidentDetailsRequest: typeof buildTrafficIncidentDetailsRequest;\n parseTrafficIncidentDetailsResponse: typeof parseTrafficIncidentDetailsResponse;\n trafficIncidentDetailsTemplate: TrafficIncidentDetailsTemplate;\n} = {\n buildTrafficIncidentDetailsRequest,\n parseTrafficIncidentDetailsResponse,\n trafficIncidentDetailsTemplate,\n};\nexport default customize;\n","/**\n * @module services-customization\n * @group Customization\n */\n\nimport alongRouteSearchCustomize from '../along-route-search/customize';\nimport autocompleteCustomize from '../autocomplete-search/customize';\nimport evChargingStationsAvailabilityCustomize from '../ev-charging-stations-availability/customize';\nimport geocodeCustomize from '../geocode/customize';\nimport geometryDataCustomize from '../geometry-data/customize';\nimport geometrySearchCustomize from '../geometry-search/customize';\nimport placeByIdCustomize from '../place-by-id/customize';\nimport reachableRangeCustomize from '../reachable-range/customize';\nimport revgeoCustomize from '../revgeo/customize';\nimport routingCustomize from '../routing/customize';\nimport trafficAreaAnalyticsCustomize from '../traffic-area-analytics/customize';\nimport trafficIncidentDetailsCustomize from '../traffic-incident-details/customize';\n\n/**\n * Access to service implementation components for advanced customization.\n *\n * This object provides low-level access to the internal components of each service,\n * allowing developers to customize request building, response parsing, and other\n * aspects of service behavior. This is useful for advanced use cases like:\n * - Custom request/response transformations\n * - Integration with custom API gateways or proxies\n * - Adding custom validation or error handling\n * - Implementing request/response logging or monitoring\n * - Adapting to custom API endpoints or versions\n *\n * @remarks\n * Most developers won't need to use this directly. The standard service functions\n * (like `geocode`, `search`, `calculateRoute`) are sufficient for typical use cases.\n * Only use customization when you need to modify the internal service behavior.\n *\n * Each service exposes:\n * - Request builders: Functions that construct API requests\n * - Response parsers: Functions that transform API responses\n * - Templates: Configuration objects defining service behavior\n * - Validation schemas: Input parameter validation rules\n *\n * @example\n * ```typescript\n * import { mergeFromGlobal } from '@tomtom-org/maps-sdk/core';\n * import { customizeService } from '@tomtom-org/maps-sdk/services';\n *\n * const { buildGeocodingRequest, parseGeocodingResponse } = customizeService.geocode;\n *\n * // Request builders expect the global configuration to be merged in already\n * const url = buildGeocodingRequest(mergeFromGlobal({ query: 'Amsterdam' }));\n *\n * // Send the request yourself, then parse the raw API payload\n * const apiResponse = await fetch(url);\n * const places = parseGeocodingResponse(await apiResponse.json());\n * ```\n *\n * @group Advanced\n */\nexport const customizeService: {\n alongRouteSearch: typeof alongRouteSearchCustomize;\n reverseGeocode: typeof revgeoCustomize;\n geocode: typeof geocodeCustomize;\n geometryData: typeof geometryDataCustomize;\n geometrySearch: typeof geometrySearchCustomize;\n calculateRoute: typeof routingCustomize;\n reachableRange: typeof reachableRangeCustomize;\n evChargingStationsAvailability: typeof evChargingStationsAvailabilityCustomize;\n trafficAreaAnalytics: typeof trafficAreaAnalyticsCustomize;\n trafficIncidentDetails: typeof trafficIncidentDetailsCustomize;\n placeByID: typeof placeByIdCustomize;\n autocompleteSearch: typeof autocompleteCustomize;\n} = {\n alongRouteSearch: alongRouteSearchCustomize,\n reverseGeocode: revgeoCustomize,\n geocode: geocodeCustomize,\n geometryData: geometryDataCustomize,\n geometrySearch: geometrySearchCustomize,\n calculateRoute: routingCustomize,\n reachableRange: reachableRangeCustomize,\n evChargingStationsAvailability: evChargingStationsAvailabilityCustomize,\n trafficAreaAnalytics: trafficAreaAnalyticsCustomize,\n trafficIncidentDetails: trafficIncidentDetailsCustomize,\n placeByID: placeByIdCustomize,\n autocompleteSearch: autocompleteCustomize,\n};\n","import type {\n ChargingPark,\n ChargingParkWithAvailability,\n ChargingStationsAvailability,\n CommonPlaceProps,\n EVChargingStationWithAvailabilityPlaceProps,\n Place,\n Places,\n} from '@tomtom-org/maps-sdk/core';\nimport { bboxFromGeoJSON } from '@tomtom-org/maps-sdk/core';\nimport { callService } from '../shared/serviceTemplate';\nimport type { EVChargingStationsAvailabilityTemplate } from './evChargingStationsAvailabilityTemplate';\nimport { evChargingStationsAvailabilityTemplate } from './evChargingStationsAvailabilityTemplate';\nimport type { ChargingStationsAvailabilityParams } from './types/evChargingStationsAvailabilityParams';\n\n/**\n * Get real-time availability of electric vehicle charging stations.\n *\n * Provides current operational status of charging points and connectors at EV charging parks,\n * enabling drivers to find available chargers before arriving at a location.\n *\n * @remarks\n * Key information returned:\n * - **Point-level status**: Available, Occupied, Reserved, Out of Service\n * - **Connector details**: Power ratings, plug types, current availability\n * - **Aggregated counts**: Quick overview of available vs occupied chargers\n * - **Access information**: Public, private, or restricted access\n * - **Opening hours**: When the charging facility is accessible\n *\n * @param params - Charging availability parameters with station ID\n * @param customTemplate - Advanced customization for request/response handling\n *\n * @returns Promise resolving to charging station availability information\n *\n * @example\n * ```typescript\n * // Get availability for a specific charging park\n * const availability = await evChargingStationsAvailability({\n * key: 'your-api-key',\n * id: 'charging-park-id-123'\n * });\n *\n * // Check how many chargers are available\n * const availableCount = availability.chargingPointAvailability.statusCounts.Available;\n * console.log(`${availableCount} chargers available`);\n *\n * // Find available CCS connectors\n * const ccsConnectors = availability.connectorAvailabilities.find(\n * ca => ca.connector.type === 'IEC62196Type2CCS'\n * );\n * ```\n *\n * @see [EV Charging Availability API](https://docs.tomtom.com/search-api/documentation)\n * @see [Places Quickstart Guide](https://docs.tomtom.com/maps-sdk-js/guides/services/places/quickstart)\n * @see [EV Charging Stations Availability Guide](https://docs.tomtom.com/maps-sdk-js/guides/services/places/ev-charging-stations-availability)\n *\n * @group EV Charging\n */\nexport const evChargingStationsAvailability = async (\n params: ChargingStationsAvailabilityParams,\n customTemplate?: Partial<EVChargingStationsAvailabilityTemplate>,\n): Promise<ChargingStationsAvailability | undefined> =>\n callService(\n params,\n { ...evChargingStationsAvailabilityTemplate, ...customTemplate },\n 'EVChargingStationsAvailability',\n );\n\n/**\n * Enhance a place with real-time EV charging availability data.\n *\n * Fetches availability information for an EV charging station and merges it into\n * the place properties.\n *\n * @param place - The place to enhance with availability data\n * @param commonParams - Common service parameters (e.g. `key`, `customServiceBaseURL`, `onAPIRequest`) forwarded to the availability request\n *\n * @returns Promise resolving to the place with merged availability information\n * @returns undefined if no availability data is present for the `place` input parameter.\n *\n * @example\n * ```typescript\n * // After search, enhance place with availability\n * const searchResult = await search({ query: 'EV charging', ... });\n * const place = searchResult.features[0];\n *\n * const enhancedPlace = await getPlaceWithEVAvailability(place);\n * const availability = enhancedPlace.properties.chargingPark?.availability;\n *\n * if (availability) {\n * console.log('Available chargers:', availability.chargingPointAvailability.count);\n * }\n * ```\n *\n * @group EV Charging\n */\nexport const getPlaceWithEVAvailability = async <P extends CommonPlaceProps = CommonPlaceProps>(\n place: Place<P>,\n commonParams?: Omit<ChargingStationsAvailabilityParams, 'id'>,\n): Promise<Place<EVChargingStationWithAvailabilityPlaceProps> | undefined> => {\n const availabilityId = place.properties.dataSources?.chargingAvailability?.id;\n if (!availabilityId) {\n return undefined;\n }\n try {\n const availability = await evChargingStationsAvailability({ ...commonParams, id: availabilityId });\n const poi = place.properties.poi;\n return availability\n ? {\n ...place,\n properties: {\n ...place.properties,\n // We override poi opening hours with the ones from the EV call, which might be better supported:\n ...(poi && { poi: { ...poi, openingHours: availability.openingHours } }),\n chargingPark: {\n ...(place.properties.chargingPark as ChargingPark),\n availability,\n },\n },\n }\n : undefined;\n } catch (e) {\n // (Likely a QPS limit error)\n console.error(e);\n return undefined;\n }\n};\n\n/**\n * Enhance multiple places with real-time EV charging availability data.\n *\n * Fetches availability information for all EV charging stations in a collection\n * and merges it into their properties. Non-EV places and EV stations without availability data are returned unchanged.\n *\n * @remarks\n * **Important**: Availability requests are made sequentially to avoid exceeding\n * API rate limits (QPS - Queries Per Second). For large result sets, this may\n * take some time.\n *\n * @param places - Collection of places to enhance\n * @param options - Configuration options, including common service parameters (e.g. `key`, `customServiceBaseURL`, `onAPIRequest`) forwarded to each availability request\n *\n * @returns Promise resolving to places collection with merged availability\n *\n * @example\n * ```typescript\n * // Search for charging stations and add availability\n * const results = await search({\n * query: 'EV charging',\n * at: [4.9, 52.3],\n * radius: 5000\n * });\n *\n * const withAvailability = await getPlacesWithEVAvailability(results, {\n * excludeIfAvailabilityUnknown: true // Filter out stations with unknown availability\n * });\n *\n * // Display only stations with known availability\n * withAvailability.features.forEach(place => {\n * const available = place.properties.chargingPark?.availability?.chargingPointAvailability.count;\n * console.log(`${place.properties.poi?.name}: ${available} chargers`);\n * });\n * ```\n *\n * @group EV Charging\n */\nexport async function getPlacesWithEVAvailability<P extends CommonPlaceProps = CommonPlaceProps>(\n places: Places<P>,\n options?: Omit<ChargingStationsAvailabilityParams, 'id'> & {\n excludeIfAvailabilityUnknown: true;\n },\n): Promise<Places<EVChargingStationWithAvailabilityPlaceProps>>;\n\nexport async function getPlacesWithEVAvailability<P extends CommonPlaceProps = CommonPlaceProps>(\n places: Places<P>,\n options?: Omit<ChargingStationsAvailabilityParams, 'id'> & {\n /**\n * If true, places with unknown availability will be filtered out. Otherwise, they will be included.\n * @default false\n */\n excludeIfAvailabilityUnknown?: false;\n },\n): Promise<Places<P | EVChargingStationWithAvailabilityPlaceProps>>;\n\nexport async function getPlacesWithEVAvailability<P extends CommonPlaceProps = CommonPlaceProps>(\n places: Places<P>,\n options?: Omit<ChargingStationsAvailabilityParams, 'id'> & {\n excludeIfAvailabilityUnknown?: boolean;\n },\n): Promise<Places<P | EVChargingStationWithAvailabilityPlaceProps>> {\n const { excludeIfAvailabilityUnknown, ...commonParams } = options ?? {};\n const enhancedPlaces: Array<Place<P> | Place<EVChargingStationWithAvailabilityPlaceProps>> = [];\n for (const place of places.features) {\n // (We fetch the availabilities sequentially on purpose to prevent QPS limit errors)\n const placeWithAvailability = await getPlaceWithEVAvailability(place, commonParams);\n if (placeWithAvailability) {\n enhancedPlaces.push(placeWithAvailability);\n } else if (!excludeIfAvailabilityUnknown) {\n enhancedPlaces.push(place);\n }\n }\n return { ...places, features: enhancedPlaces, bbox: bboxFromGeoJSON(enhancedPlaces) };\n}\n\n/**\n * Type guard to check if a ChargingPark has availability data.\n *\n * @param chargingPark - The charging park to check\n * @returns True if the charging park has availability data\n *\n * @group EV Charging\n */\nexport const hasChargingAvailability = (\n chargingPark: ChargingPark | ChargingParkWithAvailability | undefined,\n): chargingPark is ChargingParkWithAvailability =>\n Boolean(chargingPark && 'availability' in chargingPark && chargingPark.availability);\n","import { z } from 'zod';\nimport { commonSearchParamsSchema } from '../search/commonSearchParamsSchema';\nimport { commonGeocodeAndFuzzySearchParamsSchema } from '../shared/schema/commonGeocodeAndFuzzySearchParamsSchema';\nimport type { SchemaRefinement } from '../shared/types/validation';\nimport type { ExplorationSearchParams } from './types';\n\nconst positionSchema = z.array(z.number()).min(2);\n\nconst polygonSchema = z.object({\n type: z.literal('Polygon'),\n coordinates: z.array(z.array(z.array(z.number()))),\n bbox: z.array(z.number()).optional(),\n});\n\nconst multiPolygonSchema = z.object({\n type: z.literal('MultiPolygon'),\n coordinates: z.array(z.array(z.array(z.array(z.number())))),\n bbox: z.array(z.number()).optional(),\n});\n\nconst circleSchema = z.object({\n type: z.literal('Circle'),\n coordinates: positionSchema,\n radius: z.number().positive(),\n});\n\nconst polygonFeaturesSchema = z.object({\n type: z.literal('FeatureCollection'),\n features: z.array(\n z.object({\n type: z.literal('Feature'),\n geometry: z.union([polygonSchema, multiPolygonSchema]),\n properties: z.any().optional(),\n }),\n ),\n});\n\nconst geometryInputSchema = z.union([polygonSchema, multiPolygonSchema, circleSchema, polygonFeaturesSchema]);\n\nconst bboxTupleSchema = z.tuple([z.number(), z.number(), z.number(), z.number()]);\n\nconst hasBBoxObjectSchema = z.object({ bbox: bboxTupleSchema });\n\nconst hasBBoxSchema = z.union([bboxTupleSchema, hasBBoxObjectSchema]);\n\n// The places-api accepts from + size ≤ 10000 (OpenSearch's `index.max_result_window`),\n// so the exploration-search pagination bounds are wider than the generic TomTom search cap.\nconst PLACES_API_MAX_WINDOW = 10000;\n\n// When the request omits `size`, the places-api falls back to OpenSearch's default page size of 10,\n// so `from` alone still consumes 10 of the window. The refinement uses this to model wire behaviour.\nconst PLACES_API_DEFAULT_SIZE = 10;\n\nconst recordTypeSchema = z.enum(['POI', 'PointAddress', 'Street']);\n\nconst explorationSearchRequestOptional = z.object({\n municipalities: z.array(z.string()).optional(),\n boundingBoxes: z.array(hasBBoxSchema).optional(),\n geometries: z.array(geometryInputSchema).optional(),\n placeTypes: z.array(recordTypeSchema).min(1).optional(),\n areaId: z.string().min(1).optional(),\n areaTags: z.array(z.string().min(1)).min(1).optional(),\n offset: z.number().int().min(0).max(PLACES_API_MAX_WINDOW).optional(),\n limit: z.number().int().min(1).max(PLACES_API_MAX_WINDOW).optional(),\n});\n\n/**\n * @ignore\n */\nexport const explorationSearchRequestSchema = commonSearchParamsSchema.extend(\n commonGeocodeAndFuzzySearchParamsSchema.extend(explorationSearchRequestOptional.shape).shape,\n);\n\n// The places-api rejects fully unbiased searches (they fan out across the whole index),\n// so the SDK mirrors that contract by requiring at least one geographic filter.\n// `municipalities` and `areaId` are accepted as standalone biases — both pin\n// the response to a specific municipality polygon, no bbox/position needed.\nconst geoBiasRefinement: SchemaRefinement<ExplorationSearchParams> = {\n check: (data) =>\n !!data.position ||\n !!data.boundingBox ||\n !!data.boundingBoxes?.length ||\n !!data.geometries?.length ||\n !!data.municipalities?.length ||\n !!data.areaId,\n message:\n 'A geographic bias is required: provide one of position, boundingBox(es), geometries, municipalities, or areaId.',\n};\n\n// The places-api enforces `from + size ≤ 10000` (OpenSearch's\n// `index.max_result_window`). When `limit` is omitted the wire request also omits `size`,\n// and the backend falls back to its default page size — so the effective `size` on the\n// wire is `limit ?? PLACES_API_DEFAULT_SIZE`, not 0.\nconst paginationWindowRefinement: SchemaRefinement<ExplorationSearchParams> = {\n check: (data) => (data.offset ?? 0) + (data.limit ?? PLACES_API_DEFAULT_SIZE) <= PLACES_API_MAX_WINDOW,\n message: `offset + limit must not exceed ${PLACES_API_MAX_WINDOW} (the places-api pagination window; when limit is omitted the backend defaults to ${PLACES_API_DEFAULT_SIZE}).`,\n};\n\n/**\n * @ignore\n */\nexport const explorationSearchRequestValidationConfig = {\n schema: explorationSearchRequestSchema,\n refinements: [geoBiasRefinement, paginationWindowRefinement],\n};\n","import { type BBox, bboxFromGeoJSON, getPosition, poiCategoriesToID } from '@tomtom-org/maps-sdk/core';\nimport type { MultiPolygon, Polygon, Position } from 'geojson';\nimport type { Circle, SearchGeometryInput } from '../geometry-search';\nimport type { ExplorationSearchParams, ExplorationSearchPayloadAPI, ExplorationSearchRequestAPI } from './types';\n\nconst DEFAULT_NEAR_RADIUS_KM = 2;\nconst CIRCLE_POLYGON_SAMPLES = 64;\nconst EARTH_RADIUS_M = 6371000;\n\n// Experimental places-api deployment — keeping this URL hardcoded until the service graduates.\n// const EXPLORATION_SEARCH_BASE_URL = 'https://search-places-poc.braveriver-6e85d43a.westeurope.azurecontainerapps.io';\nconst EXPLORATION_SEARCH_BASE_URL = 'https://search-places-poc.whitebush-bbe9c5f7.westeurope.azurecontainerapps.io';\n\nconst buildUrlBasePath = (params: ExplorationSearchParams): string =>\n params.customServiceBaseURL ?? EXPLORATION_SEARCH_BASE_URL;\n\nconst toNearPayload = (params: ExplorationSearchParams): ExplorationSearchPayloadAPI['near'] => {\n const coordinates = getPosition(params.position);\n if (!coordinates) return undefined;\n const radiusKm =\n params.radiusMeters && params.radiusMeters > 0 ? params.radiusMeters / 1000 : DEFAULT_NEAR_RADIUS_KM;\n return { coordinates, radius_km: radiusKm };\n};\n\nconst toCategoriesPayload = (params: ExplorationSearchParams): string[] | undefined =>\n params.poiCategories?.length\n ? params.poiCategories.map((category) => String(poiCategoriesToID[category]))\n : undefined;\n\nconst toBboxesPayload = (params: ExplorationSearchParams): BBox[] | undefined => {\n const inputs = [...(params.boundingBox ? [params.boundingBox] : []), ...(params.boundingBoxes ?? [])];\n const bboxes = inputs.map((input) => bboxFromGeoJSON(input)).filter((bbox): bbox is BBox => bbox !== undefined);\n return bboxes.length ? bboxes : undefined;\n};\n\n// Samples a circle boundary into an equirectangular-approximation Polygon ring\n// so Circle inputs can be sent to a places API that only accepts GeoJSON polygons.\nconst circleToPolygon = (circle: Circle): Polygon => {\n const [lon, lat] = circle.coordinates;\n const dLatDeg = (circle.radius / EARTH_RADIUS_M) * (180 / Math.PI);\n const dLonDeg = dLatDeg / Math.cos((lat * Math.PI) / 180);\n const ring: Position[] = [];\n for (let i = 0; i < CIRCLE_POLYGON_SAMPLES; i++) {\n const angle = (2 * Math.PI * i) / CIRCLE_POLYGON_SAMPLES;\n ring.push([lon + dLonDeg * Math.cos(angle), lat + dLatDeg * Math.sin(angle)]);\n }\n ring.push(ring[0]);\n return { type: 'Polygon', coordinates: [ring] };\n};\n\nconst normalizeGeometry = (input: SearchGeometryInput): (Polygon | MultiPolygon)[] => {\n switch (input.type) {\n case 'Polygon':\n case 'MultiPolygon':\n return [input];\n case 'Circle':\n return [circleToPolygon(input)];\n case 'FeatureCollection':\n return input.features.map((feature) => feature.geometry);\n }\n};\n\n// The places-api validates Polygon/MultiPolygon rings strictly via OpenSearch and rejects:\n// - unclosed rings → \"invalid LinearRing found (coordinates are not closed)\"\n// - consecutive duplicate points → \"invalid_shape_exception: Provided shape has duplicate consecutive coordinates\"\n// Upstream sources (reachable-area outputs, isoline libraries, hand-authored polygons) regularly\n// produce either, so we normalise here — the wire request is the one place every caller converges.\nconst dedupeConsecutive = (ring: Position[]): Position[] =>\n ring.filter((point, index) => index === 0 || point[0] !== ring[index - 1][0] || point[1] !== ring[index - 1][1]);\n\nconst closeRing = (ring: Position[]): Position[] => {\n if (ring.length < 2) return ring;\n const [first, last] = [ring[0], ring[ring.length - 1]];\n return first[0] === last[0] && first[1] === last[1] ? ring : [...ring, [...first]];\n};\n\nconst sanitizeRing = (ring: Position[]): Position[] => closeRing(dedupeConsecutive(ring));\n\nconst sanitizeGeometry = <G extends Polygon | MultiPolygon>(geometry: G): G => {\n if (geometry.type === 'Polygon') {\n return { ...geometry, coordinates: geometry.coordinates.map(sanitizeRing) } as G;\n }\n return {\n ...geometry,\n coordinates: geometry.coordinates.map((polygon) => polygon.map(sanitizeRing)),\n } as G;\n};\n\nconst toGeometriesPayload = (params: ExplorationSearchParams): (Polygon | MultiPolygon)[] | undefined => {\n if (!params.geometries?.length) return undefined;\n const geometries = params.geometries.flatMap(normalizeGeometry).map(sanitizeGeometry);\n return geometries.length ? geometries : undefined;\n};\n\nconst toMunicipalitiesPayload = (params: ExplorationSearchParams): string[] | undefined =>\n params.municipalities?.length ? params.municipalities : undefined;\n\n/**\n * Default function for building an exploration search request from {@link ExplorationSearchParams}.\n * @param params The exploration search parameters, with global configuration already merged into them.\n */\nexport const buildExplorationSearchRequest = (params: ExplorationSearchParams): ExplorationSearchRequestAPI => {\n const url = new URL('/places', buildUrlBasePath(params));\n const near = toNearPayload(params);\n const categories = toCategoriesPayload(params);\n const bboxes = toBboxesPayload(params);\n const geometries = toGeometriesPayload(params);\n const municipalities = toMunicipalitiesPayload(params);\n const data: ExplorationSearchPayloadAPI = {\n ...(params.query && { q: params.query }),\n ...(params.countries?.[0] && { country: params.countries[0] }),\n ...(municipalities && { municipalities }),\n ...(params.poiBrands?.[0] && { brand: params.poiBrands[0] }),\n ...(categories && { categories }),\n ...(params.placeTypes?.length && { types: params.placeTypes }),\n ...(params.areaId && { area_id: params.areaId }),\n ...(params.areaTags?.length && { area_tags: params.areaTags }),\n ...(near && { near }),\n ...(bboxes && { bboxes }),\n ...(geometries && { geometries }),\n ...(params.offset !== undefined && { from: params.offset }),\n ...(params.limit !== undefined && { size: params.limit }),\n };\n return { url, data };\n};\n","import type { BBox, Place, PlaceType, POI, POICategory } from '@tomtom-org/maps-sdk/core';\nimport {\n bboxFromGeoJSON,\n bboxOnlyIfWithArea,\n getPosition,\n poiIDsToCategories,\n toPointGeometry,\n} from '@tomtom-org/maps-sdk/core';\nimport type { SearchSummary } from '../shared';\nimport type {\n ExplorationPlaceProps,\n ExplorationPOIAPI,\n ExplorationRecordType,\n ExplorationSearchParams,\n ExplorationSearchResponse,\n ExplorationSearchResponseAPI,\n ExplorationSearchResultAPI,\n ExplorationViewportAPI,\n} from './types';\n\n// The places-api `types` vocabulary uses `PointAddress` (no space) while the\n// SDK's shared PlaceType uses `'Point Address'` — map on the way out so\n// consumers get the same discriminator values as the other search services.\nconst API_TYPE_TO_PLACE_TYPE: Record<ExplorationRecordType, PlaceType> = {\n POI: 'POI',\n PointAddress: 'Point Address',\n Street: 'Street',\n};\n\nconst toBBoxFromViewport = (viewport: ExplorationViewportAPI | undefined): BBox | undefined => {\n if (!viewport) return undefined;\n const [[minLon, minLat], [maxLon, maxLat]] = viewport.coordinates;\n return [minLon, minLat, maxLon, maxLat];\n};\n\n// Resolve the API's category-ID fields that describe THIS place into a deduplicated POICategory[].\n// The places-api splits categories across `categorySet` (the broad taxonomy) and `preferredCategoryId`\n// (the most specific leaf for this place); we merge them so consumers see RESTAURANT *and*\n// ITALIAN_RESTAURANT, ordered with the most specific (largest) IDs first.\n//\n// `nearbyCategoryIds` is intentionally excluded — despite the suggestive name it lists categories\n// of OTHER places near this one (a \"see also\" hint), not subcategories of this place. Including\n// it pollutes downstream rendering: PlacesModule picks `categories[0]` for the marker icon, so\n// e.g. an EV charging station near a restaurant would be rendered with a fork-and-knife icon.\nconst collectCategoryIds = (poiAPI: ExplorationPOIAPI): number[] => {\n const ids = new Set<number>();\n for (const code of poiAPI.categorySet ?? []) {\n ids.add(Number(code));\n }\n if (poiAPI.preferredCategoryId) {\n ids.add(Number(poiAPI.preferredCategoryId));\n }\n return [...ids].filter((id) => Number.isFinite(id));\n};\n\nconst collectCategories = (poiAPI: ExplorationPOIAPI): POICategory[] => {\n const ids = collectCategoryIds(poiAPI);\n // Sort descending so leaf-level categories (larger numeric IDs in the TomTom taxonomy) come\n // first — useful for callers that pick `categories[0]` as a primary label.\n ids.sort((a, b) => b - a);\n const seen = new Set<POICategory>();\n const result: POICategory[] = [];\n for (const id of ids) {\n const category = poiIDsToCategories[id];\n if (category && !seen.has(category)) {\n seen.add(category);\n result.push(category);\n }\n }\n return result;\n};\n\n// POI fields live under `hit.poi` — pick and shape into the SDK's POI type.\nconst parsePoi = (poiAPI: ExplorationPOIAPI | undefined): POI | undefined => {\n if (!poiAPI?.name) return undefined;\n return {\n name: poiAPI.name,\n categories: collectCategories(poiAPI),\n localizedCategories: poiAPI.classifications?.map((classification) => classification.name) ?? [],\n ...(poiAPI.phone && { phone: poiAPI.phone }),\n ...(poiAPI.url && { url: poiAPI.url }),\n ...(poiAPI.brands?.length && { brands: poiAPI.brands.map((brand) => brand.name) }),\n ...(poiAPI.timeZone?.ianaId && { timeZone: poiAPI.timeZone }),\n };\n};\n\nconst parseResult = (result: ExplorationSearchResultAPI): Place<ExplorationPlaceProps> => {\n const { id, position, address, score, distance_m, type, viewport } = result;\n const placeType = API_TYPE_TO_PLACE_TYPE[type ?? 'POI'];\n const poi = parsePoi(result.poi);\n const bbox = toBBoxFromViewport(viewport);\n return {\n type: 'Feature',\n id,\n geometry: toPointGeometry(position),\n ...(bbox && { bbox }),\n properties: {\n type: placeType,\n ...(address && { address }),\n ...(poi && { poi }),\n ...(score !== undefined && { score }),\n ...(distance_m !== undefined && { distance: distance_m }),\n ...(result.area_id && { areaId: result.area_id }),\n ...(result.area_country && { areaCountry: result.area_country }),\n ...(result.area_tags?.length && { areaTags: result.area_tags }),\n } as ExplorationPlaceProps,\n };\n};\n\nconst buildSummary = (apiResponse: ExplorationSearchResponseAPI, params: ExplorationSearchParams): SearchSummary => {\n const hasNear = !!getPosition(params.position);\n const geoBias = hasNear ? getPosition(params.position) : null;\n return {\n query: params.query ?? '',\n queryType: hasNear ? 'NEARBY' : 'NON_NEAR',\n queryTime: 0,\n numResults: apiResponse.hits.length,\n offset: params.offset ?? 0,\n totalResults: apiResponse.total,\n fuzzyLevel: 0,\n ...(geoBias && { geoBias }),\n };\n};\n\n/**\n * Default function to parse an exploration search response.\n * @param apiResponse The API response.\n * @param params The original request parameters.\n */\nexport const parseExplorationSearchResponse = (\n apiResponse: ExplorationSearchResponseAPI,\n params: ExplorationSearchParams,\n): ExplorationSearchResponse => {\n const features = apiResponse.hits.map(parseResult);\n const bbox = bboxOnlyIfWithArea(bboxFromGeoJSON(features));\n return {\n type: 'FeatureCollection',\n properties: buildSummary(apiResponse, params),\n features,\n ...(bbox && { bbox }),\n };\n};\n","import type { ServiceTemplate } from '../shared';\nimport { post } from '../shared/fetch';\nimport { explorationSearchRequestValidationConfig } from './explorationSearchRequestSchema';\nimport { buildExplorationSearchRequest } from './requestBuilder';\nimport { parseExplorationSearchResponse } from './responseParser';\nimport type {\n ExplorationSearchParams,\n ExplorationSearchRequestAPI,\n ExplorationSearchResponse,\n ExplorationSearchResponseAPI,\n} from './types';\n\n/**\n * Exploration search service template type.\n * @ignore\n */\nexport type ExplorationSearchTemplate = ServiceTemplate<\n ExplorationSearchParams,\n ExplorationSearchRequestAPI,\n ExplorationSearchResponseAPI,\n ExplorationSearchResponse\n>;\n\n/**\n * Exploration search service template main implementation.\n * @ignore\n */\nexport const explorationSearchTemplate: ExplorationSearchTemplate = {\n requestValidation: explorationSearchRequestValidationConfig,\n buildRequest: buildExplorationSearchRequest,\n sendRequest: post,\n parseResponse: parseExplorationSearchResponse,\n};\n","import { callService } from '../shared/serviceTemplate';\nimport type { ExplorationSearchTemplate } from './explorationSearchTemplate';\nimport { explorationSearchTemplate } from './explorationSearchTemplate';\nimport type { ExplorationSearchParams, ExplorationSearchResponse } from './types';\n\n/**\n * Search for places via the exploration places API.\n *\n * The exploration search service exposes the same input and output surface as\n * the existing {@link search} service, but targets the exploration places API\n * (POST `/places` with a JSON body) instead of the TomTom Search API.\n *\n * @remarks\n * Key features:\n * - **Fuzzy name match** via {@link ExplorationSearchParams.query}\n * - **Near filter** via `position` + `radiusMeters` (mapped to `near.coordinates` + `near.radius_km`)\n * - **Bounding box** via `boundingBox` (mapped to `bboxes`)\n * - **Geometries** via {@link ExplorationSearchParams.geometries} (Polygon,\n * MultiPolygon, Circle, or FeatureCollection — same shape as {@link geometrySearch})\n * - **Brand / category / country / municipalities** filters\n * - **Pagination** via `offset` / `limit` (mapped to `from` / `size`)\n *\n * @param params Exploration search parameters\n * @param customTemplate Advanced customization for request/response handling\n *\n * @returns Promise resolving to a collection of matching places, with the same\n * {@link SearchPlaceProps} shape used by the existing search service.\n *\n * @example\n * ```typescript\n * // Nearest restaurants to Dam Square\n * const results = await explorationSearch({\n * position: [4.9003, 52.3791],\n * radiusMeters: 5000,\n * poiCategories: ['RESTAURANT'],\n * limit: 10,\n * });\n *\n * // Inside a GeoJSON polygon\n * const inArea = await explorationSearch({\n * geometries: [{\n * type: 'Polygon',\n * coordinates: [[\n * [4.878, 52.364], [4.922, 52.364],\n * [4.922, 52.394], [4.878, 52.394],\n * [4.878, 52.364],\n * ]],\n * }],\n * poiCategories: ['RESTAURANT'],\n * });\n * ```\n *\n * @ignore\n * @experimental\n */\nexport const explorationSearch = async (\n params: ExplorationSearchParams,\n customTemplate?: Partial<ExplorationSearchTemplate>,\n): Promise<ExplorationSearchResponse> =>\n callService(params, { ...explorationSearchTemplate, ...customTemplate }, 'ExplorationSearch');\n","/**\n * Canonical area-tag vocabulary used by the places-api. Tokens are\n * lowercase `snake_case` and describe the character of the municipality\n * polygon a hit sits in (currently populated for `DE` / `NL` / `FR`).\n *\n * Two groups are exposed below:\n *\n * - **Populated tokens** — the 46 tokens the current pipeline run actually\n * emits, grouped by classification axis (settlement scale, density,\n * terrain, tourism, etc.). These are what every live `area_tags` value\n * will be drawn from today.\n * - **Reserved tokens** — vocabulary tokens that exist canonically but\n * are not populated by the current pipeline run. They may appear in\n * future runs without an API change, so callers should treat them as\n * valid even though no hit returns them today.\n *\n * The schema doc warns the vocabulary may shift between pipeline\n * iterations, so SDK consumers should code defensively. The\n * {@link AreaTag} alias falls back to `string` to keep arbitrary\n * (yet-to-be-added) tokens type-compatible without losing IDE\n * autocompletion on the known set.\n *\n * @ignore\n * @experimental\n */\nexport const POPULATED_AREA_TAGS = [\n // Settlement scale\n 'village',\n 'small_town',\n 'mid_town',\n 'suburban',\n // Density\n 'rural',\n 'semi_rural',\n 'residential',\n // Setting (terrain)\n 'inland',\n 'coastal',\n 'atlantic_coast',\n 'alpine',\n 'mountain',\n 'foothills',\n 'forest',\n 'plains',\n 'farmland',\n 'agricultural',\n // Tourism intensity\n 'no_tourism',\n 'low_tourism',\n 'moderate_tourism',\n 'tourism_economy',\n // Tourism type\n 'eco_tourism',\n 'heritage_tourism',\n 'watersports',\n // Outdoor recreation\n 'hiking',\n 'cycling',\n // Economy\n 'industrial',\n 'logistics_hub',\n 'retail_hub',\n 'shopping_district',\n 'hospital_town',\n 'dining_scene',\n // Transport\n 'car_oriented',\n 'walkable',\n 'transit_connected',\n 'transit_sparse',\n 'highway_corridor',\n 'commuter_town',\n // Traffic\n 'low_traffic',\n 'moderate_traffic',\n // Commercial character\n 'sparse_commercial',\n // Macro region\n 'western_europe',\n 'central_europe',\n 'southern_europe',\n 'north_american',\n 'iberian',\n] as const;\n\n/**\n * Vocabulary tokens that exist canonically in the area-tag taxonomy but\n * aren't emitted by the current pipeline run (per the schema doc's\n * \"tokens that exist… but are not populated\" list). Filtering on these\n * today returns zero hits, but a future pipeline run may activate them\n * without a wire-protocol change.\n *\n * @ignore\n * @experimental\n */\nexport const RESERVED_AREA_TAGS = [\n // Water / coastal sub-types\n 'lakefront',\n 'riverine',\n 'wetland',\n 'marina',\n 'harbor',\n 'beach',\n // Macro region (Mediterranean)\n 'mediterranean',\n // Destination archetypes\n 'ski_destination',\n 'beach_destination',\n 'wine_tourism',\n 'vineyard',\n 'university_town',\n 'medieval_old_town',\n 'unesco_site',\n // US sub-regions (mentioned by name in the doc)\n 'midwest_us',\n 'rust_belt',\n] as const;\n\n/**\n * Union of every populated area-tag token currently emitted by the\n * places-api pipeline.\n *\n * @ignore\n * @experimental\n */\nexport type PopulatedAreaTag = (typeof POPULATED_AREA_TAGS)[number];\n\n/**\n * Union of every canonical (populated + reserved) area-tag token. Reserved\n * tokens may begin appearing as the pipeline evolves.\n *\n * @ignore\n * @experimental\n */\nexport type CanonicalAreaTag = PopulatedAreaTag | (typeof RESERVED_AREA_TAGS)[number];\n\n/**\n * Area-tag token accepted by the places-api `area_tags` filter and\n * returned on each hit's `areaTags` property.\n *\n * Resolves to the canonical {@link CanonicalAreaTag} union for editor\n * autocompletion, but tolerates any other string so the SDK doesn't\n * hard-error on tokens added in a future pipeline run. To strictly\n * constrain inputs to the currently-known vocabulary, use\n * {@link CanonicalAreaTag} (or {@link PopulatedAreaTag}) directly.\n *\n * @example\n * ```typescript\n * // Autocomplete on the canonical vocabulary, but new tokens still type-check.\n * const tags: AreaTag[] = ['walkable', 'transit_connected'];\n * ```\n *\n * @ignore\n * @experimental\n */\nexport type AreaTag = CanonicalAreaTag | string;\n","import type { Place } from '@tomtom-org/maps-sdk/core';\nimport { callService } from '../shared/serviceTemplate';\nimport type { GeocodingTemplate } from './geocodingTemplate';\nimport { geocodingTemplate } from './geocodingTemplate';\nimport type { GeocodingParams } from './types/geocodingParams';\nimport type { GeocodingProps, GeocodingResponse } from './types/geocodingResponse';\n\n/**\n * Convert addresses into geographic coordinates (geocoding).\n *\n * The Geocode service translates addresses and place names into geographic coordinates,\n * enabling you to position markers on maps, calculate routes, or perform spatial analysis.\n *\n * @remarks\n * This service is optimized for address lookup and does not return POIs (Points of Interest).\n * For POI search, use the {@link search} function instead.\n *\n * Features:\n * - Highly tolerant of typos and incomplete addresses\n * - Handles various address formats and components\n * - Supports street addresses, intersections, and cross streets\n * - Works with higher-level geographies (cities, counties, states, countries)\n * - Returns structured address components\n *\n * @param params - Geocoding parameters including the address query\n * @param customTemplate - Advanced customization for request/response handling\n *\n * @returns Promise resolving to geocoded location results\n *\n * @example\n * ```typescript\n * // Geocode a complete address\n * const result = await geocode({\n * key: 'your-api-key',\n * query: '1600 Pennsylvania Avenue NW, Washington, DC'\n * });\n *\n * // Geocode with partial address\n * const partialResult = await geocode({\n * key: 'your-api-key',\n * query: 'Amsterdam, Netherlands'\n * });\n *\n * // Geocode with bias towards specific location\n * const biasedResult = await geocode({\n * key: 'your-api-key',\n * query: 'Main Street',\n * at: [4.9041, 52.3676], // Bias toward Amsterdam\n * limit: 5\n * });\n *\n * // Geocode an intersection\n * const intersection = await geocode({\n * key: 'your-api-key',\n * query: '5th Avenue & 42nd Street, New York'\n * });\n * ```\n *\n * @see [Geocode API Documentation](https://docs.tomtom.com/search-api/documentation/geocoding-service/geocode)\n * @see [Places Quickstart Guide](https://docs.tomtom.com/maps-sdk-js/guides/services/places/quickstart)\n * @see [Geocoding Guide](https://docs.tomtom.com/maps-sdk-js/guides/services/places/geocoding)\n *\n * @group Geocoding\n */\nexport const geocode = async (\n params: GeocodingParams,\n customTemplate?: Partial<GeocodingTemplate>,\n): Promise<GeocodingResponse> => callService(params, { ...geocodingTemplate, ...customTemplate }, 'Geocode');\n\n/**\n * Geocode a query and return the single best-matching place.\n *\n * Convenience wrapper around {@link geocode} that returns the top result directly.\n *\n * @param query - Address or place name to geocode\n * @returns Promise resolving to the best-matching place\n * @throws {Error} If no results are found for the query\n *\n * @remarks\n * Use {@link geocode} with a `limit` parameter when you need to handle the case\n * where no results are found without throwing.\n *\n * @example\n * ```typescript\n * const place = await geocodeOne('Amsterdam Centraal');\n * console.log(place.geometry.coordinates); // [lng, lat]\n * ```\n *\n * @group Geocoding\n */\nexport const geocodeOne = async (query: string): Promise<Place<GeocodingProps>> => {\n const result = await geocode({ query, limit: 1 });\n if (!result.features[0]) throw new Error(`geocodeOne: no results found for \"${query}\"`);\n return result.features[0];\n};\n","import type { CommonPlaceProps, Places, PolygonFeatures } from '@tomtom-org/maps-sdk/core';\nimport { callService } from '../shared/serviceTemplate';\nimport type { GeometryDataTemplate } from './geometryDataTemplate';\nimport { geometryDataTemplate } from './geometryDataTemplate';\nimport type { GeometryDataParams, GeometryParams, GeometryPlaceParams } from './types/geometryDataParams';\n\n/**\n * Merge our internal Places \"properties\" response with Geometry data\n * @param places\n * @param geometries\n * @returns FeatureCollection<Polygon | MultiPolygon>,\n */\nconst mergePlacesWithGeometries = (places: Places, geometries: PolygonFeatures): PolygonFeatures<CommonPlaceProps> => {\n const placesIdMap = places.features.reduce(\n (acc, place) => {\n const geometryId = place.properties.dataSources?.geometry?.id;\n\n if (geometryId) {\n acc[geometryId] = {\n ...place.properties,\n placeCoordinates: place.geometry.coordinates,\n };\n }\n return acc;\n },\n {} as Record<string, unknown>,\n );\n\n const features = geometries.features.map((feature) => {\n if (feature.id && placesIdMap[feature.id]) {\n return { ...feature, properties: placesIdMap[feature.id] };\n }\n\n return feature;\n });\n\n return {\n type: 'FeatureCollection',\n bbox: geometries.bbox,\n features,\n } as PolygonFeatures<CommonPlaceProps>;\n};\n\n/**\n * Retrieve polygon geometries representing geographic area boundaries.\n *\n * The Geometry Data service returns coordinate sets that define the outlines of\n * geographic areas such as cities, countries, administrative regions, or POI footprints.\n * These polygons enable visualization of area boundaries, spatial analysis, and\n * geofencing applications.\n *\n * @remarks\n * Key features:\n * - **Batch requests**: Fetch up to 20 geometries in a single call\n * - **Multiple scales**: From countries down to building footprints\n * - **Place integration**: Can merge with place data for enriched results\n * - **Standard GeoJSON**: Returns standard Polygon/MultiPolygon features\n *\n * Common use cases:\n * - Display city or country boundaries on maps\n * - Show POI building footprints\n * - Create geofences for spatial queries\n * - Visualize administrative divisions\n * - Calculate areas and spatial relationships\n *\n * @param params - Geometry parameters with IDs or places to fetch boundaries for\n * @param customTemplate - Advanced customization for request/response handling\n *\n * @returns Promise resolving to polygon features representing area boundaries\n *\n * @example\n * ```typescript\n * // Fetch geometry by ID\n * const cityBoundary = await geometryData({\n * key: 'your-api-key',\n * geometries: ['geometry-id-123']\n * });\n *\n * // Fetch multiple geometries at once\n * const boundaries = await geometryData({\n * key: 'your-api-key',\n * geometries: ['country-id-1', 'city-id-2', 'poi-id-3']\n * });\n *\n * // Fetch and merge with place data\n * const searchResults = await search({ query: 'Amsterdam' });\n * const withBoundaries = await geometryData({\n * key: 'your-api-key',\n * geometries: searchResults // Places with geometry IDs\n * });\n * // Result includes both place properties and polygon boundaries\n * ```\n *\n * @see [Geometry Data API Documentation](https://docs.tomtom.com/search-api/documentation/additional-data-service/additional-data)\n * @see [Places Quickstart Guide](https://docs.tomtom.com/maps-sdk-js/guides/services/places/quickstart)\n * @see [Geometry Data Guide](https://docs.tomtom.com/maps-sdk-js/guides/services/places/geometry-data)\n *\n * @group Geometry\n */\nexport async function geometryData(\n params: GeometryDataParams,\n customTemplate?: Partial<GeometryDataTemplate>,\n): Promise<PolygonFeatures>;\nexport async function geometryData(\n params: GeometryPlaceParams,\n customTemplate?: Partial<GeometryDataTemplate>,\n): Promise<PolygonFeatures<CommonPlaceProps>>;\nexport async function geometryData(params: GeometryParams, customTemplate?: Partial<GeometryDataTemplate>) {\n const geometryResult = await callService(params, { ...geometryDataTemplate, ...customTemplate }, 'GeometryData');\n\n // If params.geometries is a FeatureCollection(Place), the properties will be merged with geometry results.\n if (!Array.isArray(params.geometries) && params.geometries.type === 'FeatureCollection') {\n return mergePlacesWithGeometries(params.geometries, geometryResult);\n }\n\n return geometryResult;\n}\n","import { callService } from '../shared/serviceTemplate';\nimport type { PlaceByIdTemplate } from './placeByIdTemplate';\nimport { placeByIdTemplate } from './placeByIdTemplate';\nimport type { PlaceByIdParams, PlaceByIdResponse } from './types';\n\n/**\n * Retrieve detailed information about a place using its unique identifier.\n *\n * The Place by ID service fetches comprehensive data for a specific place when you\n * have its ID from a previous search or from a place's dataSources. This is useful\n * for getting additional details or refreshing information about a known location.\n *\n * @remarks\n * Use cases:\n * - **Fetch POI details**: Get extended information like reviews, photos, amenities\n * - **Refresh place data**: Update information for a cached place\n * - **Deep linking**: Allow users to share/bookmark specific places\n * - **Related POI navigation**: Explore parent/child relationships\n *\n * The ID can be obtained from:\n * - Previous search results (place.id)\n * - POI details data source (place.properties.dataSources.poiDetails.id)\n * - Related POIs (place.properties.relatedPois[].id)\n * - Deep links or bookmarks\n *\n * @param params - Place by ID parameters with the place identifier\n * @param customTemplate - Advanced customization for request/response handling\n *\n * @returns Promise resolving to detailed place information\n *\n * @example\n * ```typescript\n * // Get place by ID from search result\n * const searchResult = await search({ query: 'Eiffel Tower' });\n * const placeId = searchResult.features[0].id;\n *\n * const placeDetails = await placeById({\n * key: 'your-api-key',\n * entityId: placeId\n * });\n *\n * // Get extended POI details\n * const place = searchResult.features[0];\n * const poiDetailsId = place.properties.dataSources?.poiDetails?.id;\n *\n * if (poiDetailsId) {\n * const detailedPOI = await placeById({\n * key: 'your-api-key',\n * entityId: poiDetailsId\n * });\n * // May include additional photos, reviews, extended hours, etc.\n * }\n *\n * // Navigate to related POI\n * const relatedPOI = place.properties.relatedPois?.[0];\n * if (relatedPOI) {\n * const parentPlace = await placeById({\n * key: 'your-api-key',\n * entityId: relatedPOI.id\n * });\n * console.log('Parent location:', parentPlace.properties.address);\n * }\n * ```\n *\n * @see [Place by ID API Documentation](https://docs.tomtom.com/search-api/documentation/place-by-id-service/place-by-id)\n * @see [Places Quickstart Guide](https://docs.tomtom.com/maps-sdk-js/guides/services/places/quickstart)\n *\n * @group Place\n */\nexport const placeById = async (\n params: PlaceByIdParams,\n customTemplate?: Partial<PlaceByIdTemplate>,\n): Promise<PlaceByIdResponse> => callService(params, { ...placeByIdTemplate, ...customTemplate }, 'PlaceById');\n","import type { Language } from '@tomtom-org/maps-sdk/core';\nimport type { POICategoryResult } from './types';\n\nconst cache = new Map<Language, POICategoryResult[]>();\nconst textCache = new Map<Language, Record<string, POICategoryResult>>();\n\nconst DEFAULT_LANGUAGE: Language = 'en-GB';\n\nconst getWithDefault = (language: Language | undefined): Language => language ?? DEFAULT_LANGUAGE;\n\nexport const normalizeText = (text: string): string => text.toLowerCase().replaceAll(/[\\s\\-_,]+/g, '');\n\n/** @ignore */\nexport const getCachedCategories = (language: Language | undefined): POICategoryResult[] | undefined => {\n return cache.get(getWithDefault(language));\n};\n\n/** @ignore */\nexport const setCachedCategories = (language: Language | undefined, categories: POICategoryResult[]): void => {\n const lang = getWithDefault(language);\n cache.set(lang, categories);\n textCache.set(lang, buildTextCache(categories));\n};\n\n/** @ignore */\nexport const buildTextCache = (categories: POICategoryResult[]): Record<string, POICategoryResult> => {\n const cache: Record<string, POICategoryResult> = {};\n for (const category of categories) {\n const texts = [category.name, ...category.synonyms].map(normalizeText);\n for (const text of texts) {\n cache[text] = category;\n }\n }\n return cache;\n};\n\n/** @ignore */\nexport const getCachedTextEntries = (language: Language | undefined): Record<string, POICategoryResult> =>\n textCache.get(language ?? DEFAULT_LANGUAGE) ?? {};\n","import { z } from 'zod';\nimport { commonServiceRequestSchema } from '../shared/schema/commonParamsSchema';\n\n/**\n * @ignore\n */\nexport const poiCategoriesRequestSchema = commonServiceRequestSchema.extend({\n filters: z.array(z.string()).optional(),\n});\n","import { PLACES_URL_PATH } from '../shared/request/commonSearchRequestBuilder';\nimport { appendCommonParams } from '../shared/request/requestBuildingUtils';\nimport type { POICategoriesParams } from './types';\n\n/**\n * Builds the POI categories request URL from the merged params.\n * The `filters` param is intentionally omitted — it is applied client-side.\n * @ignore\n */\nexport const buildPoiCategoriesRequest = (params: POICategoriesParams): URL => {\n const baseUrl = params.customServiceBaseURL ?? `${params.commonBaseURL}${PLACES_URL_PATH}/poiCategories.json`;\n const url = new URL(baseUrl);\n appendCommonParams(url.searchParams, params);\n return url;\n};\n","import { type POICategory, poiIDsToCategories } from '@tomtom-org/maps-sdk/core';\nimport type { POICategoriesResponse, POICategoryResult, PoiCategoriesResponseAPI } from './types';\n\nconst parsePoiCategory = (api: PoiCategoriesResponseAPI['poiCategories'][number]): POICategoryResult | null => {\n const code = poiIDsToCategories[api.id];\n if (!code) {\n console.error(\n `Received unknown POI category ID ${api.id} (${api.name}). This category will be skipped and unavailable for filtering.`,\n );\n return null;\n }\n return {\n code,\n name: api.name,\n childCategoryCodes: api.childCategoryIds\n .map((id) => poiIDsToCategories[id])\n .filter((c): c is POICategory => !!c),\n synonyms: api.synonyms,\n };\n};\n\n/**\n * @ignore\n */\nexport const parsePoiCategoriesResponse = (apiResponse: PoiCategoriesResponseAPI): POICategoriesResponse => ({\n poiCategories: apiResponse.poiCategories.map(parsePoiCategory).filter((c) => c !== null),\n});\n","import type { ServiceTemplate } from '../shared';\nimport { get } from '../shared/fetch';\nimport { poiCategoriesRequestSchema } from './poiCategoriesRequestSchema';\nimport { buildPoiCategoriesRequest } from './requestBuilder';\nimport { parsePoiCategoriesResponse } from './responseParser';\nimport type { POICategoriesParams, POICategoriesResponse, PoiCategoriesResponseAPI } from './types';\n\n/**\n * @ignore\n */\nexport type PoiCategoriesTemplate = ServiceTemplate<\n POICategoriesParams,\n URL,\n PoiCategoriesResponseAPI,\n POICategoriesResponse\n>;\n\n/**\n * @ignore\n */\nexport const poiCategoriesTemplate: PoiCategoriesTemplate = {\n requestValidation: { schema: poiCategoriesRequestSchema },\n buildRequest: buildPoiCategoriesRequest,\n getAPIVersion: () => 1,\n sendRequest: get,\n parseResponse: parsePoiCategoriesResponse,\n};\n","import type { Language, POICategory } from '@tomtom-org/maps-sdk/core';\nimport { mergeFromGlobal } from '@tomtom-org/maps-sdk/core';\nimport { callService } from '../shared/serviceTemplate';\nimport { getCachedCategories, getCachedTextEntries, normalizeText, setCachedCategories } from './cache';\nimport { poiCategoriesTemplate } from './poiCategoriesTemplate';\nimport type { POICategoriesParams, POICategoriesResponse, POICategoryResult } from './types';\n\n/**\n * Filters cached categories for `language` against `filters` keyword strings.\n * Each filter is normalized and matched as a substring of category names and synonyms.\n * Results are merged, deduplicated by code, and pruned of child categories whose parent also matched.\n *\n * @param language - Language key for the cache lookup.\n * @param filters - Keyword strings to match against names and synonyms.\n */\nconst filterCategories = (language: Language | undefined, filters: string[]): POICategoryResult[] => {\n const cachedTextEntries = getCachedTextEntries(language);\n const resultsByCode = new Map<POICategory, POICategoryResult>();\n for (const filterTerm of filters) {\n const normalizedFilterInput = normalizeText(filterTerm);\n for (const [text, category] of Object.entries(cachedTextEntries)) {\n // if the category text includes the filter input, add the category to the results:\n if (text.includes(normalizedFilterInput) && category) {\n resultsByCode.set(category.code, category);\n }\n }\n }\n\n // Remove child categories whose parent also matched — the parent already implies them.\n for (const category of resultsByCode.values()) {\n for (const childCode of category.childCategoryCodes) {\n resultsByCode.delete(childCode);\n }\n }\n return [...resultsByCode.values()];\n};\n\n/**\n * Retrieve the list of POI category types supported by the TomTom Search API.\n *\n * The returned `code` values (`POICategory`) serve two purposes:\n * - Pass to `search({ poiCategories })` to filter search results by category.\n * - Pass to `POIsModule.filterCategories()` to filter the map's built-in POI icons.\n * Generic parent codes (e.g. `'RESTAURANT'`) are supported there; overly specific child\n * codes (e.g. `'ITALIAN_RESTAURANT'`) are not — use a parent or a `POICategoryGroup` instead.\n *\n * Results are cached in memory per language. The first call fetches from the API;\n * all subsequent calls — including filtered ones — are served from the cache.\n *\n * @param params - Optional parameters. Uses global `TomTomConfig` by default.\n *\n * @example\n * ```typescript\n * // All categories\n * const { poiCategories: all } = await getPOICategories();\n *\n * // Filtered by keyword, merged and deduplicated\n * const { poiCategories: gyms } = await getPOICategories({ filters: ['gym'] });\n * gyms.forEach(c => console.log(c.code, c.name, c.childCategoryCodes));\n * ```\n *\n * @group POI Categories\n */\nexport const getPOICategories = async (params: POICategoriesParams = {}): Promise<POICategoriesResponse> => {\n const { language } = mergeFromGlobal(params);\n\n let allCategories = getCachedCategories(language);\n\n if (!allCategories) {\n const response = await callService(params, poiCategoriesTemplate, 'POICategories');\n allCategories = response.poiCategories;\n setCachedCategories(language, allCategories);\n }\n\n if (params.filters?.length) {\n return { poiCategories: filterCategories(language, params.filters) };\n }\n\n return { poiCategories: allCategories };\n};\n\n/**\n * Convenience wrapper around {@link getPOICategories} that returns a flat array of `POICategory` codes.\n *\n * The returned codes serve two purposes:\n * - Pass to `search({ poiCategories })` to filter search results by category.\n * - Pass to `POIsModule.filterCategories()` to filter the map's built-in POI icons.\n * Generic parent codes (e.g. `'RESTAURANT'`) are supported there; overly specific child\n * codes (e.g. `'ITALIAN_RESTAURANT'`) are not — use a parent or a `POICategoryGroup` instead.\n *\n * Accepts the same parameters as {@link getPOICategories} — use `filters` to narrow by keyword.\n *\n * @param params - Optional parameters. Uses global `TomTomConfig` by default.\n *\n * @example\n * ```typescript\n * // All category codes\n * const codes = await getPOICategoryCodes();\n *\n * // Codes matching 'restaurant' — for search or generic map POI filtering\n * const restaurantCodes = await getPOICategoryCodes({ filters: ['restaurant'] });\n * ```\n *\n * @group POI Categories\n */\nexport const getPOICategoryCodes = async (params: POICategoriesParams = {}): Promise<POICategory[]> => {\n const { poiCategories: categories } = await getPOICategories(params);\n return categories.map((category) => category.code);\n};\n","import { bboxFromGeoJSON, PolygonFeature, PolygonFeatures } from '@tomtom-org/maps-sdk/core';\nimport { SDKServiceError } from '../shared/errors';\nimport { callService } from '../shared/serviceTemplate';\nimport type { ReachableRangeTemplate } from './reachableRangeTemplate';\nimport { reachableRangeTemplate } from './reachableRangeTemplate';\nimport type { ReachableRangeParams } from './types/reachableRangeParams';\n\n/**\n * Calculate the area reachable from a starting point within given constraints.\n *\n * The Reachable Range service (also known as isochrone) computes a polygon representing\n * all locations that can be reached from an origin within specified limits of time, distance,\n * or energy consumption. This is essential for range analysis, service area visualization,\n * and logistics planning.\n *\n * @remarks\n * Key features:\n * - **Time-based ranges**: Areas reachable within a time budget (e.g., 30 minutes)\n * - **Distance-based ranges**: Areas within a distance limit (e.g., 50km)\n * - **Energy-based ranges**: EV range considering battery consumption\n * - **Traffic awareness**: Accounts for current and historic traffic patterns\n * - **Vehicle-specific**: Considers vehicle characteristics and constraints\n *\n * Common use cases:\n * - **Service area mapping**: \"Show areas we can deliver to within 1 hour\"\n * - **EV range visualization**: Display drivable range on current battery\n * - **Real estate**: \"Find homes within 45 minutes of workplace\"\n * - **Emergency services**: Calculate ambulance/fire truck response areas\n * - **Market analysis**: Identify customer catchment areas\n * - **Location planning**: Optimize facility placement based on coverage\n *\n * @param params Reachable range parameters including origin and budget constraints\n * @param customTemplate Advanced customization for request/response handling\n *\n * @returns Promise resolving to a polygon representing the reachable area\n *\n * @example\n * ```typescript\n * // Calculate 30-minute drive time range\n * const range30min = await calculateReachableRange({\n * key: 'your-api-key',\n * origin: [4.9041, 52.3676], // Amsterdam\n * timeBudgetInSec: 1800 // 30 minutes\n * });\n *\n * // Calculate 50km distance range\n * const range50km = await calculateReachableRange({\n * key: 'your-api-key',\n * origin: [4.9041, 52.3676],\n * distanceBudgetInMeters: 50000\n * });\n *\n * // EV range based on battery\n * const evRange = await calculateReachableRange({\n * key: 'your-api-key',\n * origin: [4.9041, 52.3676],\n * fuelBudgetInkWh: 20, // Remaining battery\n * vehicleEngineType: 'electric',\n * constantSpeedConsumptionInkWhPerHundredkm: [[50, 8], [80, 12]]\n * });\n *\n * // Traffic-aware range at departure time\n * const morningRange = await calculateReachableRange({\n * key: 'your-api-key',\n * origin: [4.9041, 52.3676],\n * timeBudgetInSec: 2700, // 45 minutes\n * departAt: new Date('2025-10-20T08:00:00Z') // Rush hour\n * });\n * ```\n *\n * @see [Reachable Range API Documentation](https://docs.tomtom.com/routing-api/documentation/routing/calculate-reachable-range)\n * @see [Routing Quickstart Guide](https://docs.tomtom.com/maps-sdk-js/guides/services/routing/quickstart)\n *\n * @group Routing\n */\nexport const calculateReachableRange = async (\n params: ReachableRangeParams,\n customTemplate?: Partial<ReachableRangeTemplate>,\n): Promise<PolygonFeature<ReachableRangeParams>> =>\n callService(params, { ...reachableRangeTemplate, ...customTemplate }, 'Reachable Range');\n\n/**\n * Calculate multiple reachable range areas from different origins or with different constraints.\n *\n * Computes several isochrone polygons in sequence, useful for comparing ranges from multiple\n * locations, visualizing multiple time/distance rings from the same origin, or analyzing\n * different vehicle scenarios.\n *\n * @remarks\n * Use cases:\n * - **Multi-location comparison**: Compare service areas of different store locations\n * - **Concentric ranges**: Create 15/30/45 minute drive time rings from one origin\n * - **Scenario analysis**: Compare ranges for different vehicle types or times of day\n * - **Coverage optimization**: Find optimal facility locations with minimal overlap\n *\n * Note: Ranges are calculated sequentially to avoid rate limiting. For large batches,\n * consider implementing your own parallel processing with appropriate throttling.\n *\n * @param paramsArray Array of reachable range parameters, one for each area to calculate\n * @param options.signal An `AbortSignal` to cancel in-flight requests between iterations.\n * @param customTemplate Advanced customization for request/response handling\n *\n * @returns Promise resolving to a FeatureCollection of reachable area polygons\n *\n * @example\n * ```typescript\n * // Calculate concentric drive time rings (15, 30, 45 minutes)\n * const timeRings = await calculateReachableRanges([\n * { key: 'your-api-key', origin: [4.9, 52.3], timeBudgetInSec: 900 },\n * { key: 'your-api-key', origin: [4.9, 52.3], timeBudgetInSec: 1800 },\n * { key: 'your-api-key', origin: [4.9, 52.3], timeBudgetInSec: 2700 }\n * ]);\n *\n * // Compare service areas of multiple stores\n * const storeRanges = await calculateReachableRanges([\n * { key: 'your-api-key', origin: [4.9, 52.3], timeBudgetInSec: 1800 },\n * { key: 'your-api-key', origin: [4.5, 51.9], timeBudgetInSec: 1800 },\n * { key: 'your-api-key', origin: [5.1, 52.1], timeBudgetInSec: 1800 }\n * ]);\n *\n * // Compare EV ranges at different battery levels\n * const batteryRanges = await calculateReachableRanges([\n * {\n * key: 'your-api-key',\n * origin: [4.9, 52.3],\n * fuelBudgetInkWh: 10,\n * vehicleEngineType: 'electric'\n * },\n * {\n * key: 'your-api-key',\n * origin: [4.9, 52.3],\n * fuelBudgetInkWh: 20,\n * vehicleEngineType: 'electric'\n * }\n * ]);\n * ```\n *\n * @see [Reachable Range API Documentation](https://docs.tomtom.com/routing-api/documentation/routing/calculate-reachable-range)\n * @see [Routing Quickstart Guide](https://docs.tomtom.com/maps-sdk-js/guides/services/routing/quickstart)\n *\n * @group Routing\n */\nexport const calculateReachableRanges = async (\n paramsArray: ReachableRangeParams[],\n options?: { signal?: AbortSignal },\n customTemplate?: Partial<ReachableRangeTemplate>,\n): Promise<PolygonFeatures<ReachableRangeParams>> => {\n const features: PolygonFeature<ReachableRangeParams>[] = [];\n for (const params of paramsArray) {\n // Throws AbortError if a newer call has cancelled this one before the next request starts\n options?.signal?.throwIfAborted();\n try {\n // we sequentially fetch reachable ranges (less speed but better to prevent QPS limit breaches):\n features.push(await calculateReachableRange(params, customTemplate));\n } catch (error) {\n // Re-throw non-API errors (e.g. validation/programming errors) and critical HTTP errors\n // (403 Forbidden, 429 Too Many Requests) — only silently skip API errors that indicate\n // no reachable range could be computed at this location (e.g. no road network).\n if (!(error instanceof SDKServiceError) || error.status === 403 || error.status === 429) {\n throw error;\n }\n }\n }\n const bbox = bboxFromGeoJSON(features);\n return { type: 'FeatureCollection', ...(bbox && { bbox }), features };\n};\n","import type { Place, RevGeoAddressProps } from '@tomtom-org/maps-sdk/core';\nimport { callService } from '../shared/serviceTemplate';\nimport type { ReverseGeocodingTemplate } from './reverseGeocodingTemplate';\nimport { reverseGeocodingTemplate } from './reverseGeocodingTemplate';\nimport type { ReverseGeocodingParams } from './types/reverseGeocodingParams';\n\n/**\n * Response from the reverse geocoding service.\n *\n * Contains a place with address information for the given coordinates.\n *\n * @group Reverse Geocoding\n */\nexport type ReverseGeocodingResponse = Place<RevGeoAddressProps>;\n\n/**\n * Convert geographic coordinates into human-readable addresses (reverse geocoding).\n *\n * Reverse geocoding translates latitude/longitude coordinates into street addresses,\n * which is essential for location-based applications that need to display addresses\n * from GPS coordinates or map clicks.\n *\n * @remarks\n * Common use cases:\n * - **Tracking applications**: Convert GPS coordinates from devices into addresses\n * - **Map interactions**: Display address when user clicks on map\n * - **Location sharing**: Show readable location instead of coordinates\n * - **Delivery apps**: Confirm pickup/dropoff addresses from driver location\n * - **Asset tracking**: Display current location of vehicles or equipment\n *\n * Features:\n * - Returns complete address hierarchy (street, city, state, country)\n * - Returns the result type (address, street, or geography)\n * - Narrows results to administrative areas with `geographyType`\n * - Direction-aware street matching with `heading`\n *\n * @param params - Reverse geocoding parameters including coordinates\n * @param customTemplate - Advanced customization for request/response handling\n *\n * @returns Promise resolving to the address for the given coordinates\n *\n * @example\n * ```typescript\n * // Get address for coordinates\n * const address = await reverseGeocode({\n * key: 'your-api-key',\n * position: [4.9041, 52.3676] // Amsterdam coordinates\n * });\n * // Returns: Dam, 1012 Amsterdam, Netherlands\n *\n * // Get the address matched along a given travel direction\n * const specificAddress = await reverseGeocode({\n * key: 'your-api-key',\n * position: [-77.0369, 38.8977], // Washington DC\n * heading: 90 // Traveling east\n * });\n *\n * // Get the containing municipality instead of the street\n * const municipality = await reverseGeocode({\n * key: 'your-api-key',\n * position: [-74.0060, 40.7128], // New York\n * geographyType: ['Municipality']\n * });\n * ```\n *\n * @see [Reverse Geocode API Documentation](https://docs.tomtom.com/search-api/documentation/reverse-geocoding-service/reverse-geocode)\n * @see [Places Quickstart Guide](https://docs.tomtom.com/maps-sdk-js/guides/services/places/quickstart)\n * @see [Reverse Geocoding Guide](https://docs.tomtom.com/maps-sdk-js/guides/services/places/reverse-geocoding)\n *\n * @group Reverse Geocoding\n */\nexport const reverseGeocode = async (\n params: ReverseGeocodingParams,\n customTemplate?: Partial<ReverseGeocodingTemplate>,\n): Promise<ReverseGeocodingResponse> =>\n callService(params, { ...reverseGeocodingTemplate, ...customTemplate }, 'ReverseGeocode');\n","import type { Routes } from '@tomtom-org/maps-sdk/core';\nimport { callService } from '../shared/serviceTemplate';\nimport type { CalculateRouteTemplate } from './calculateRouteTemplate';\nimport { calculateRouteTemplate } from './calculateRouteTemplate';\nimport type { CalculateRouteParams } from './types/calculateRouteParams';\n\n/**\n * Calculates a route between an origin and destination, optionally passing through extra waypoints.\n *\n * @param params - Route calculation parameters including locations, travel mode, and other options\n * @param customTemplate - Optional template customization for request/response handling\n * @returns Promise resolving to calculated route(s) with geometry, distance, and travel time\n *\n * @see [Calculate Route API Documentation](https://docs.tomtom.com/routing-api/documentation/tomtom-maps/calculate-route)\n *\n * @group Routing\n */\nexport const calculateRoute = async (\n params: CalculateRouteParams,\n customTemplate?: Partial<CalculateRouteTemplate>,\n): Promise<Routes> => callService(params, { ...calculateRouteTemplate, ...customTemplate }, 'Routing');\n","import { z } from 'zod';\nimport { commonSearchParamsSchema } from '../search/commonSearchParamsSchema';\nimport { commonGeocodeAndFuzzySearchParamsSchema } from '../shared/schema/commonGeocodeAndFuzzySearchParamsSchema';\n\nconst fuzzySearchRequestOptional = z.object({\n minFuzzyLevel: z.number().min(1).max(4).optional(),\n maxFuzzyLevel: z.number().min(1).max(4).optional(),\n});\n\n/**\n * @ignore\n */\nexport const fuzzySearchRequestSchema = commonSearchParamsSchema.extend(\n commonGeocodeAndFuzzySearchParamsSchema.extend(fuzzySearchRequestOptional.shape).shape,\n);\n","import { bboxFromGeoJSON } from '@tomtom-org/maps-sdk/core';\nimport { arrayToCSV } from '../shared/arrays';\nimport { appendCommonSearchParams, PLACES_URL_PATH } from '../shared/request/commonSearchRequestBuilder';\nimport { appendByJoiningParamValue, appendOptionalParam } from '../shared/request/requestBuildingUtils';\nimport type { FuzzySearchParams } from './types';\n\nconst buildUrlBasePath = (params: FuzzySearchParams): string =>\n params.customServiceBaseURL ?? `${params.commonBaseURL}${PLACES_URL_PATH}/search/${params.query ?? ''}.json`;\n\n/**\n * Default function for building a fuzzy search request from {@link FuzzySearchParams}\n * @param params The fuzzy search parameters, with global configuration already merged into them.\n */\nexport const buildFuzzySearchRequest = (params: FuzzySearchParams): URL => {\n const url = new URL(`${buildUrlBasePath(params)}`);\n appendCommonSearchParams(url, params);\n const urlParams = url.searchParams;\n appendOptionalParam(urlParams, 'typeahead', params.typeahead);\n appendOptionalParam(urlParams, 'ofs', params.offset);\n appendByJoiningParamValue(urlParams, 'countrySet', params.countries);\n appendOptionalParam(urlParams, 'radius', params.radiusMeters);\n const bbox = params.boundingBox && bboxFromGeoJSON(params.boundingBox);\n if (bbox) {\n urlParams.append('topLeft', arrayToCSV([bbox[3], bbox[0]]));\n urlParams.append('btmRight', arrayToCSV([bbox[1], bbox[2]]));\n }\n appendOptionalParam(urlParams, 'minFuzzyLevel', params.minFuzzyLevel);\n appendOptionalParam(urlParams, 'maxFuzzyLevel', params.maxFuzzyLevel);\n return url;\n};\n","import { bboxFromGeoJSON, bboxOnlyIfWithArea } from '@tomtom-org/maps-sdk/core';\nimport { latLonAPIToPosition } from '../shared/geometry';\nimport { parseSearchAPIResult, parseSummaryAPI } from '../shared/searchResultParsing';\nimport type { FuzzySearchResponse, FuzzySearchResponseAPI, QueryIntent, QueryIntentAPI } from './types';\n\nconst queryIntentApiToSdk = (intentApi: QueryIntentAPI): QueryIntent => {\n let intent;\n switch (intentApi.type) {\n case 'COORDINATE':\n intent = { ...intentApi, details: { position: latLonAPIToPosition(intentApi.details) } };\n break;\n case 'NEARBY':\n intent = {\n ...intentApi,\n details: {\n position: latLonAPIToPosition({ lon: intentApi.details.lon, lat: intentApi.details.lat }),\n text: intentApi.details.text,\n query: intentApi.details.query,\n },\n };\n break;\n case 'BOOKMARK':\n case 'W3W':\n intent = intentApi;\n }\n return intent;\n};\n\n/**\n * Default function to parse a fuzzy search response.\n * @param apiResponse The API response.\n */\nexport const parseFuzzySearchResponse = (apiResponse: FuzzySearchResponseAPI): FuzzySearchResponse => {\n const features = apiResponse.results.map(parseSearchAPIResult);\n const bbox = bboxOnlyIfWithArea(bboxFromGeoJSON(features));\n return {\n type: 'FeatureCollection',\n properties: {\n ...parseSummaryAPI(apiResponse.summary),\n queryIntent: apiResponse.summary.queryIntent.map(queryIntentApiToSdk),\n },\n features,\n ...(bbox && { bbox }),\n };\n};\n","import type { ServiceTemplate } from '../shared';\nimport { get } from '../shared/fetch';\nimport { fuzzySearchRequestSchema } from './fuzzySearchRequestSchema';\nimport { buildFuzzySearchRequest } from './requestBuilder';\nimport { parseFuzzySearchResponse } from './responseParser';\nimport type { FuzzySearchParams, FuzzySearchResponse, FuzzySearchResponseAPI } from './types';\n\n/**\n * Fuzzy search service template type.\n * @ignore\n */\nexport type FuzzySearchTemplate = ServiceTemplate<FuzzySearchParams, URL, FuzzySearchResponseAPI, FuzzySearchResponse>;\n\n/**\n * Fuzzy search service template main implementation.\n * @ignore\n */\nexport const fuzzySearchTemplate: FuzzySearchTemplate = {\n requestValidation: { schema: fuzzySearchRequestSchema },\n buildRequest: buildFuzzySearchRequest,\n sendRequest: get,\n parseResponse: parseFuzzySearchResponse,\n};\n","import { callService } from '../shared/serviceTemplate';\nimport type { FuzzySearchTemplate } from './fuzzySearchTemplate';\nimport { fuzzySearchTemplate } from './fuzzySearchTemplate';\nimport type { FuzzySearchParams, FuzzySearchResponse } from './types';\n\n/**\n * Search for places using free-text queries with fuzzy matching.\n *\n * The Fuzzy Search service provides a flexible search that handles typos, abbreviations,\n * and incomplete addresses. It searches across POIs (Points of Interest), addresses,\n * and geographic areas to find the best matches for your query.\n *\n * @remarks\n * Key features:\n * - **Typo tolerance**: Handles misspellings and typing errors\n * - **Partial matching**: Works with incomplete queries\n * - **Multi-category search**: Searches POIs, addresses, and places simultaneously\n * - **Position bias**: Prioritizes results near a given location\n * - **Flexible input**: Accepts natural language queries\n *\n * The service is ideal for:\n * - User-facing search boxes where typos are common\n * - Location lookup without knowing exact names\n * - General \"find anything\" search functionality\n * - Autocomplete with final selection\n *\n * @param params Fuzzy search parameters including the search query\n * @param customTemplate Advanced customization for request/response handling\n *\n * @returns Promise resolving to a collection of matching places\n *\n * @example\n * ```typescript\n * // Basic search with typo tolerance\n * const results = await fuzzySearch({\n * query: 'amstrdam' // Typo: missing 'e'\n * });\n * // Still finds \"Amsterdam\"\n *\n * // Search near a specific location\n * const nearby = await fuzzySearch({\n * query: 'pizza',\n * position: [4.9041, 52.3676], // Amsterdam\n * radiusMeters: 2000, // Within 2km\n * limit: 10\n * });\n *\n * // Search with category filter\n * const restaurants = await fuzzySearch({\n * poiCategories: ['ITALIAN_RESTAURANT'],\n * position: [4.9041, 52.3676]\n * });\n *\n * // Partial address search restricted to a country\n * const addresses = await fuzzySearch({\n * query: '123 main st',\n * countries: ['US'],\n * limit: 5\n * });\n * ```\n *\n * @see [Fuzzy Search API Documentation](https://docs.tomtom.com/search-api/documentation/search-service/fuzzy-search)\n * @see [Places Quickstart Guide](https://docs.tomtom.com/maps-sdk-js/guides/services/places/quickstart)\n * @see [Search Guide](https://docs.tomtom.com/maps-sdk-js/guides/services/places/search)\n *\n * @ignore (exposed via 'search')\n */\nexport const fuzzySearch = async (\n params: FuzzySearchParams,\n customTemplate?: Partial<FuzzySearchTemplate>,\n): Promise<FuzzySearchResponse> => callService(params, { ...fuzzySearchTemplate, ...customTemplate }, 'FuzzySearch');\n","import type { Place, Places, SearchPlaceProps } from '@tomtom-org/maps-sdk/core';\nimport type { AlongRouteSearchParams } from '../along-route-search';\nimport { alongRouteSearch } from '../along-route-search/alongRouteSearch';\nimport type { AlongRouteSearchTemplate } from '../along-route-search/alongRouteSearchTemplate';\nimport type { FuzzySearchParams, QueryIntent } from '../fuzzy-search';\nimport { fuzzySearch } from '../fuzzy-search/fuzzySearch';\nimport type { FuzzySearchTemplate } from '../fuzzy-search/fuzzySearchTemplate';\nimport type { GeometrySearchParams } from '../geometry-search';\nimport { geometrySearch } from '../geometry-search/geometrySearch';\nimport type { GeometrySearchTemplate } from '../geometry-search/geometrySearchTemplate';\nimport type { SearchSummary } from '../shared';\n\ntype SearchFeatureCollectionProps = SearchSummary & {\n queryIntent?: QueryIntent[];\n};\n\n/**\n * Search service response containing places that match the query.\n *\n * Collection of place features with search-specific properties like relevance scores and distances.\n *\n * @group Search\n */\nexport type SearchResponse = Places<SearchPlaceProps, SearchFeatureCollectionProps>;\n\n/**\n * Universal search function for finding places by text query or within geometries.\n *\n * This is a unified interface that automatically routes to either:\n * - **Geometry Search**: When geometries parameter is provided (search within specific areas)\n * - **Fuzzy Search**: When no geometries provided (free-text search)\n *\n * @remarks\n * The search service provides:\n * - POI (Points of Interest) search\n * - Address search\n * - Geographic area search\n * - Category-based filtering\n * - Position-based relevance ranking\n *\n * Results are ranked by relevance with scores and optional distances.\n *\n * @param params - Search parameters (either GeometrySearchParams or FuzzySearchParams)\n * @param customTemplate - Advanced customization for request/response handling\n *\n * @returns Promise resolving to a collection of matching places\n *\n * @example\n * ```typescript\n * // Free-text search near a location\n * const results = await search({\n * query: 'pizza restaurant',\n * position: [4.9041, 52.3676], // Amsterdam\n * limit: 10\n * });\n *\n * // Search within a specific area\n * const areaResults = await search({\n * query: 'coffee shop',\n * geometries: [polygon], // Search within this polygon\n * limit: 20\n * });\n *\n * // Category search\n * const restaurants = await search({\n * poiCategories: ['ITALIAN_RESTAURANT'],\n * position: [4.9041, 52.3676],\n * radiusMeters: 5000 // Within 5km\n * });\n * ```\n *\n * @see [Search API Documentation](https://docs.tomtom.com/search-api/documentation/search-service/search-service)\n * @see [Places Quickstart Guide](https://docs.tomtom.com/maps-sdk-js/guides/services/places/quickstart)\n * @see [Search Guide](https://docs.tomtom.com/maps-sdk-js/guides/services/places/search)\n *\n * @group Search\n */\nexport const search = async (\n params: GeometrySearchParams | FuzzySearchParams | AlongRouteSearchParams,\n customTemplate?: Partial<GeometrySearchTemplate | FuzzySearchTemplate | AlongRouteSearchTemplate>,\n): Promise<SearchResponse> => {\n if ('route' in params) return alongRouteSearch(params, customTemplate as Partial<AlongRouteSearchTemplate>);\n if ('geometries' in params) return geometrySearch(params, customTemplate as Partial<GeometrySearchTemplate>);\n return fuzzySearch(params, customTemplate as Partial<FuzzySearchTemplate>);\n};\n\n/**\n * Search for a single place by text query.\n *\n * Convenience function that calls {@link search} and returns the first result.\n * Throws an error if no results are found.\n *\n * @param query - Search query string\n * @returns Promise resolving to the first matching place\n * @throws {Error} If no results are found for the query\n *\n * @example\n * ```typescript\n * const place = await searchOne('Vondelpark Amsterdam');\n * console.log(place.properties.poi?.name);\n * ```\n *\n * @remarks\n * * Useful to quickly find a single place, particularly a POI.\n * * If you want to find a single address, consider 'geocodeOne'.\n *\n * @group Search\n */\nexport const searchOne = async (query: string): Promise<Place<SearchPlaceProps>> => {\n const result = await search({ query, limit: 1 });\n if (!result.features[0]) throw new Error(`searchOne: no results found for \"${query}\"`);\n return result.features[0];\n};\n","import type { TrafficAreaAnalytics } from '@tomtom-org/maps-sdk/core';\nimport { callService } from '../shared/serviceTemplate';\nimport type { TrafficAreaAnalyticsTemplate } from './trafficAreaAnalyticsTemplate';\nimport { trafficAreaAnalyticsTemplate } from './trafficAreaAnalyticsTemplate';\nimport type { TrafficAreaAnalyticsParams } from './types/trafficAreaAnalyticsParams';\n\n/**\n * Run a synchronous traffic area analytics report for a polygon region.\n *\n * Submits a single Polygon region with a date range and requested metrics, and\n * receives the full analysis result immediately (no polling required).\n *\n * @remarks\n * **Lite report constraints:**\n * - Only a single region (Polygon Feature) may be submitted per request.\n * - The date range (`startDate` → `endDate`) must not exceed 31 days.\n *\n * **Key data returned per region:**\n * - `baseData`: overall aggregated metrics for the full analysis period\n * - `timedData`: time-series breakdown at yearly / monthly / weekly / daily / hourly granularity\n * - `tiledData`: per-tile heatmap metrics (when available)\n * - `anomalies`: detected traffic anomalies keyed by camelCase metric key (e.g. `anomalies.speed`)\n *\n * **Available metrics (`metrics`):**\n * - `speed` — average speed on the road network (km/h)\n * - `freeFlowSpeed` — average speed under uncongested conditions (km/h)\n * - `congestionLevel` — percentage increase in travel time above free-flow\n * - `travelTime` — average travel time per 10 km (minutes)\n * - `networkLength` — total length of road segments with data (meters)\n *\n * @param params - Traffic Area Analytics parameters\n * @param customTemplate - Advanced customization for request/response handling\n *\n * @returns Promise resolving to a {@link TrafficAreaAnalytics} FeatureCollection\n *\n * @example\n * ```typescript\n * // Basic speed and congestion analysis for Amsterdam\n * const result = await trafficAreaAnalytics({\n * startDate: new Date('2024-08-06'),\n * endDate: new Date('2024-08-06'),\n * functionalRoadClasses: ['MOTORWAY', 'MAJOR_ROAD', 'OTHER_MAJOR_ROAD', 'SECONDARY_ROAD', 'LOCAL_CONNECTING_ROAD', 'LOCAL_ROAD_HIGH_IMPORTANCE'],\n * hours: [7, 8, 9, 17, 18],\n * metrics: ['speed', 'congestionLevel'],\n * geometry: {\n * type: 'Polygon',\n * coordinates: [\n * [[4.896128, 52.382402], [4.875701, 52.368459], [4.923611, 52.36341], [4.896128, 52.382402]]\n * ]\n * }\n * });\n *\n * const region = result.features[0];\n * console.log(region.properties.baseData.speed); // avg speed (km/h)\n * console.log(region.properties.baseData.congestionLevel); // congestion %\n * console.log(region.properties.timedData.hourly); // hourly breakdown\n * ```\n *\n * @example\n * ```typescript\n * // Full metrics over a multi-day period\n * const result = await trafficAreaAnalytics({\n * startDate: new Date('2024-08-01'),\n * endDate: new Date('2024-08-07'),\n * functionalRoadClasses: 'all',\n * hours: 'all',\n * metrics: ['speed', 'freeFlowSpeed', 'congestionLevel', 'travelTime', 'networkLength'],\n * geometry: {\n * type: 'Polygon',\n * coordinates: [[[4.89, 52.37], [4.91, 52.37], [4.91, 52.39], [4.89, 52.39], [4.89, 52.37]]]\n * }\n * });\n *\n * result.features[0].properties.timedData.daily?.forEach(day => {\n * console.log(day.date, day.congestionLevel);\n * });\n * ```\n *\n * @see [Area Analytics API Documentation](https://developer.tomtom.com/area-analytics/documentation/api/analysis-lite)\n *\n * @group Traffic\n */\nexport const trafficAreaAnalytics = async (\n params: TrafficAreaAnalyticsParams,\n customTemplate?: Partial<TrafficAreaAnalyticsTemplate>,\n): Promise<TrafficAreaAnalytics> =>\n callService(params, { ...trafficAreaAnalyticsTemplate, ...customTemplate }, 'TrafficAreaAnalytics');\n","import type { TrafficIncidentDetails } from '@tomtom-org/maps-sdk/core';\nimport { callService } from '../shared/serviceTemplate';\nimport type { TrafficIncidentDetailsTemplate } from './trafficIncidentDetailsTemplate';\nimport { trafficIncidentDetailsTemplate } from './trafficIncidentDetailsTemplate';\nimport type { TrafficIncidentDetailsParams } from './types/trafficIncidentDetailsParams';\n\n/**\n * Fetch detailed information about traffic incidents.\n *\n * Query incidents either by a geographic bounding box or by a list of incident IDs.\n * Results are GeoJSON Features whose geometry is a `Point` (localised incidents) or\n * a `LineString` (incidents spanning a stretch of road).\n *\n * @remarks\n * **Two query modes:**\n *\n * - **Bounding box** (`bbox`): Returns all incidents within the given area.\n * Maximum area is 10,000 km².\n * - **By IDs** (`ids`): Returns the specified incidents directly.\n * GET is used automatically for up to 5 IDs; POST for up to 100 IDs.\n *\n * **Key data provided per incident:**\n * - Category (`iconCategory`): type of incident (accident, roadworks, jam, etc.)\n * - Delay magnitude: severity of the delay (`minor`, `moderate`, `major`, …)\n * - Geometry: exact location or extent of the incident on the road network\n * - Time information: start/end times and whether the incident is `present` or `future`\n * - Events: one or more detailed event descriptions\n *\n * @param params - Traffic Incident Details parameters (bbox or ids)\n * @param customTemplate - Advanced customization for request/response handling\n *\n * @returns Promise resolving to a {@link TrafficIncidentDetails} object containing the matching incidents\n *\n * @example\n * ```typescript\n * // Query by bounding box (Amsterdam area)\n * const result = await trafficIncidentDetails({\n * bbox: [4.728, 52.278, 5.080, 52.479]\n * });\n *\n * result.incidents.forEach(incident => {\n * console.log(incident.properties.iconCategory); // 'accident' | 'jam' | …\n * console.log(incident.properties.magnitudeOfDelay); // 'minor' | 'major' | …\n * console.log(incident.geometry); // Point or LineString\n * });\n * ```\n *\n * @example\n * ```typescript\n * // Query up to 5 specific incidents by ID (GET)\n * const result = await trafficIncidentDetails({\n * ids: ['incident-id-1', 'incident-id-2']\n * });\n * ```\n *\n * @example\n * ```typescript\n * // Query many incident IDs — POST is used automatically when ids.length > 5\n * const result = await trafficIncidentDetails({\n * ids: largeIdArray\n * });\n * ```\n *\n * @example\n * ```typescript\n * // Filter by category and include future incidents\n * const result = await trafficIncidentDetails({\n * bbox: [4.728, 52.278, 5.080, 52.479],\n * categoryFilter: ['accident', 'road-closed'],\n * timeValidityFilter: ['present', 'future']\n * });\n * ```\n *\n * @see [Incident Details API Documentation](https://docs.tomtom.com/traffic-api/documentation/tomtom-orbis-maps/traffic-incidents/incident-details)\n * @see [Incident Details Guide](https://docs.tomtom.com/maps-sdk-js/guides/services/traffic/incident-details)\n *\n * @group Traffic\n */\nexport const trafficIncidentDetails = async (\n params: TrafficIncidentDetailsParams,\n customTemplate?: Partial<TrafficIncidentDetailsTemplate>,\n): Promise<TrafficIncidentDetails> =>\n callService(params, { ...trafficIncidentDetailsTemplate, ...customTemplate }, 'TrafficIncidentDetails');\n"],"mappings":"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I,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAA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"}
1
+ {"version":3,"file":"services.es.js","names":[],"sources":["../src/shared/types/apiResponseErrorTypes.ts","../src/shared/errors.ts","../src/shared/schema/commonParamsSchema.ts","../src/shared/schema/validation.ts","../src/shared/serviceTemplate.ts","../src/shared/fetch.ts","../src/shared/schema/geometriesSchema.ts","../src/shared/schema/commonPlacesParamsSchema.ts","../src/search/commonSearchParamsSchema.ts","../src/along-route-search/alongRouteSearchRequestSchema.ts","../src/shared/request/requestBuildingUtils.ts","../src/shared/request/commonSearchRequestBuilder.ts","../src/along-route-search/requestBuilder.ts","../src/ev-charging-stations-availability/connectorAvailability.ts","../src/shared/ev.ts","../src/shared/geometry.ts","../src/shared/searchResultParsing.ts","../src/along-route-search/responseParser.ts","../src/along-route-search/alongRouteSearchTemplate.ts","../src/along-route-search/alongRouteSearch.ts","../src/autocomplete-search/autocompleteSearchRequestSchema.ts","../src/autocomplete-search/requestBuilder.ts","../src/autocomplete-search/responseParser.ts","../src/autocomplete-search/autocompleteSearchTemplate.ts","../src/autocomplete-search/autocompleteSearch.ts","../src/along-route-search/customize.ts","../src/autocomplete-search/customize.ts","../src/ev-charging-stations-availability/evChargingStationsAvailabilityRequestSchema.ts","../src/shared/types/commonRoutingParams.ts","../src/shared/types/vehicleRestrictionParams.ts","../src/ev-charging-stations-availability/evChargingStationsAvailabilityResponseErrorParser.ts","../src/ev-charging-stations-availability/requestBuilder.ts","../src/ev-charging-stations-availability/responseParser.ts","../src/ev-charging-stations-availability/evChargingStationsAvailabilityTemplate.ts","../src/ev-charging-stations-availability/customize.ts","../src/shared/schema/commonGeocodeAndFuzzySearchParamsSchema.ts","../src/geocode/geocodingRequestSchema.ts","../src/shared/arrays.ts","../src/geocode/requestBuilder.ts","../src/geocode/responseParser.ts","../src/geocode/geocodingTemplate.ts","../src/geocode/customize.ts","../src/geometry-data/geometryDataRequestSchema.ts","../src/geometry-data/requestBuilder.ts","../src/geometry-data/responseParser.ts","../src/geometry-data/geometryDataTemplate.ts","../src/geometry-data/customize.ts","../src/geometry-search/geometrySearchRequestSchema.ts","../src/geometry-search/requestBuilder.ts","../src/geometry-search/responseParser.ts","../src/geometry-search/geometrySearchTemplate.ts","../src/geometry-search/geometrySearch.ts","../src/geometry-search/customize.ts","../src/place-by-id/placeByIdSchema.ts","../src/place-by-id/requestBuilder.ts","../src/place-by-id/responseParser.ts","../src/place-by-id/placeByIdTemplate.ts","../src/place-by-id/customize.ts","../src/routing/routingResponseErrorParser.ts","../src/shared/schema/vehicleParamsSchema.ts","../src/shared/schema/commonRoutingRequestSchema.ts","../src/reachable-range/types/reachableRangeParams.ts","../src/reachable-range/reachableRangeRequestSchema.ts","../src/shared/request/routingVehicleParamsBuilder.ts","../src/shared/request/commonRoutingRequestBuilder.ts","../src/reachable-range/requestBuilder.ts","../src/reachable-range/responseParser.ts","../src/reachable-range/reachableRangeTemplate.ts","../src/reachable-range/customize.ts","../src/revgeo/requestBuilder.ts","../src/shared/iso2ToIso3.ts","../src/revgeo/responseParser.ts","../src/revgeo/revGeocodeRequestSchema.ts","../src/revgeo/reverseGeocodingTemplate.ts","../src/revgeo/customize.ts","../src/routing/types/calculateRouteParams.ts","../src/routing/calculateRouteRequestSchema.ts","../src/routing/requestBuilder.ts","../src/routing/responseParser.ts","../src/routing/calculateRouteTemplate.ts","../src/routing/customize.ts","../src/traffic-area-analytics/types/trafficAreaAnalyticsParams.ts","../src/traffic-area-analytics/requestBuilder.ts","../src/traffic-area-analytics/responseParser.ts","../src/traffic-area-analytics/trafficAreaAnalyticsRequestSchema.ts","../src/traffic-area-analytics/trafficAreaAnalyticsTemplate.ts","../src/traffic-area-analytics/customize.ts","../src/traffic-incident-details/requestBuilder.ts","../src/traffic-incident-details/responseParser.ts","../src/traffic-incident-details/trafficIncidentDetailsRequestSchema.ts","../src/traffic-incident-details/trafficIncidentDetailsTemplate.ts","../src/traffic-incident-details/customize.ts","../src/customize/index.ts","../src/ev-charging-stations-availability/evChargingStationsAvailability.ts","../src/exploration-search/explorationSearchRequestSchema.ts","../src/exploration-search/requestBuilder.ts","../src/exploration-search/responseParser.ts","../src/exploration-search/explorationSearchTemplate.ts","../src/exploration-search/explorationSearch.ts","../src/exploration-search/types/areaTags.ts","../src/geocode/geocoding.ts","../src/geometry-data/geometryData.ts","../src/place-by-id/placeById.ts","../src/poi-categories/cache.ts","../src/poi-categories/poiCategoriesRequestSchema.ts","../src/poi-categories/requestBuilder.ts","../src/poi-categories/responseParser.ts","../src/poi-categories/poiCategoriesTemplate.ts","../src/poi-categories/poiCategories.ts","../src/reachable-range/calculateReachableRange.ts","../src/revgeo/reverseGeocoding.ts","../src/routing/calculateRoute.ts","../src/fuzzy-search/fuzzySearchRequestSchema.ts","../src/fuzzy-search/requestBuilder.ts","../src/fuzzy-search/responseParser.ts","../src/fuzzy-search/fuzzySearchTemplate.ts","../src/fuzzy-search/fuzzySearch.ts","../src/search/search.ts","../src/traffic-area-analytics/trafficAreaAnalytics.ts","../src/traffic-incident-details/trafficIncidentDetails.ts"],"sourcesContent":["/**\n * @ignore\n */\nexport interface BaseAPIResponseErrorBody {\n /*\n * HTTP error code.\n */\n httpStatusCode: number;\n /*\n * Detailed information about the error.\n */\n detailedError: {\n /*\n * One of the defined error codes.\n */\n code: string;\n /*\n * A human-readable representation of the error code.\n */\n message: string;\n /*\n * Target of the particular error.\n * Value: The name of the request parameter.\n */\n target: string;\n };\n}\n\n/**\n * @ignore\n */\nexport interface DefaultAPIResponseErrorBody extends BaseAPIResponseErrorBody {\n /*\n * A human-readable description of the error code.\n */\n error?: string;\n errorText?: string;\n}\n\n/**\n * @ignore\n */\nexport interface RoutingAPIResponseError {\n /*\n * \tThe format version\n */\n formatVersion: string;\n error: {\n /*\n * A human-readable representation of the error code.\n */\n description: string;\n };\n detailedError: {\n /*\n * A human-readable representation of the error code.\n */\n message: string;\n /*\n * One of the defined error codes.\n */\n code: string;\n };\n}\n\n/**\n * Error returned by an API.\n * @ignore\n */\nexport type APIErrorResponse<T = DefaultAPIResponseErrorBody> = {\n status?: number;\n message: string;\n data?: T;\n};\n\n/**\n * @ignore\n */\nexport enum APICode {\n TOO_MANY_REQUESTS = 429,\n FORBIDDEN = 403,\n}\n","import type { ZodIssue } from 'zod';\nimport type { ValidationError } from './schema/validation';\nimport type { ParseResponseError } from './serviceTypes';\nimport type { APIErrorResponse, DefaultAPIResponseErrorBody } from './types/apiResponseErrorTypes';\nimport { APICode } from './types/apiResponseErrorTypes';\nimport type { ServiceName } from './types/servicesTypes';\n\n/**\n * Base error class for all SDK-related errors.\n *\n * The SDK handles two distinct categories of errors:\n * 1. **Programming errors**: Configuration or usage errors in the user's application,\n * such as passing incorrect types to parameters or functions.\n * 2. **API errors**: Recoverable errors that occur during SDK operations,\n * such as network failures or invalid API responses.\n *\n * @example\n * ```typescript\n * try {\n * // SDK operation\n * } catch (error) {\n * if (error instanceof SDKError) {\n * console.error(`Error in ${error.service}: ${error.message}`);\n * }\n * }\n * ```\n *\n * @group Errors\n */\nexport class SDKError extends Error {\n /**\n * Creates a new SDKError instance.\n *\n * @param message - Human-readable error description\n * @param service - Name of the service where the error occurred\n * @param issues - Optional array of Zod validation issues for detailed error information\n */\n constructor(\n message: string,\n readonly service: string,\n readonly issues?: ZodIssue[],\n ) {\n super(message);\n\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, SDKError);\n }\n }\n}\n\n/**\n * Mapping of API error codes to human-readable error messages.\n *\n * Provides standardized error messages for common API response codes,\n * ensuring consistent error handling across the SDK.\n *\n * @group Errors\n */\nexport const APIErrorCode: { readonly [K in APICode as number]: string } = {\n [APICode.TOO_MANY_REQUESTS]: 'Too Many Requests: The API Key is over QPS (Queries per second)',\n [APICode.FORBIDDEN]: 'Request failed with status code 403',\n};\n\n/**\n * Error class for HTTP API response errors.\n *\n * Extends {@link SDKError} to include HTTP status codes and automatically\n * maps known error codes to user-friendly messages using {@link APIErrorCode}.\n *\n * @example\n * ```typescript\n * throw new SDKServiceError('Invalid request', 'geocoding', 400);\n * ```\n *\n * @group Errors\n */\nexport class SDKServiceError extends SDKError {\n /**\n * HTTP status code of the failed API request.\n *\n * Common values:\n * - `400`: Bad Request\n * - `403`: Forbidden\n * - `429`: Too Many Requests\n * - `500`: Internal Server Error\n */\n status?: number;\n\n /**\n * Creates a new SDKServiceError instance.\n *\n * If the status code matches a known error in {@link APIErrorCode},\n * the message will be automatically replaced with the standardized message.\n *\n * @param message - Error message from the API or custom message\n * @param service - Name of the service that generated the error\n * @param status - HTTP status code of the failed request\n */\n constructor(message: string, service: string, status?: number) {\n super(message, service);\n this.status = status;\n\n /*\n * We use as message what returns from API if any, otherwise we have our APIErrorCode as a fallback\n * Check if there is a status and if the status exists in the mapped API error types\n */\n if (this.status && APIErrorCode[this.status]) {\n this.message = APIErrorCode[this.status];\n }\n }\n}\n\n/**\n * @ignore\n * @param error\n * @param serviceName\n */\nexport const parseDefaultResponseError: ParseResponseError<DefaultAPIResponseErrorBody> = (error, serviceName) => {\n const { data, message, status } = error;\n // Different services uses property error or errorText or detailedError\n // Here we cover all situations as a default error parser\n const errorMessage = data?.error || data?.errorText || message;\n return new SDKServiceError(errorMessage, serviceName, status);\n};\n\n/**\n * @ignore\n * Generate error for APIResponse, any other error type will be returned as it is.\n * @param error The error captured by a catch function.\n * @param serviceName The name of the service.\n * @param parseResponseError\n */\nexport const buildResponseError = (\n error: unknown,\n serviceName: ServiceName,\n parseResponseError?: ParseResponseError<unknown>,\n): SDKError => {\n if ((error as APIErrorResponse).status) {\n const fetchError = error as APIErrorResponse;\n if (parseResponseError) {\n return parseResponseError(fetchError, serviceName);\n }\n return parseDefaultResponseError(fetchError, serviceName);\n }\n\n return new SDKError((error as Error).message, serviceName);\n};\n\n/**\n * @ignore\n * @param error\n * @param serviceName\n */\nexport const buildValidationError = (error: ValidationError, serviceName: ServiceName): SDKError =>\n new SDKError(error.message, serviceName, error.issues);\n","import { z } from 'zod';\n\n/**\n * @ignore\n */\nexport const commonServiceRequestSchema = z.object({\n apiKey: z.string().optional(),\n commonBaseURL: z.string().optional(),\n customServiceBaseURL: z.string().optional(),\n language: z.string().optional(),\n});\n","import type { ZodError } from 'zod';\nimport type { CommonServiceParams } from '../serviceTypes';\nimport type { RequestValidationConfig } from '../types/validation';\nimport { commonServiceRequestSchema } from './commonParamsSchema';\n\ntype ZodIssue = ZodError['issues'][number];\n\n/**\n * Format a Zod error into a human-readable string\n * @ignore\n */\nconst formatZodIssue = (issue: ZodIssue, depth = 0): string => {\n const path = issue.path.length > 0 ? `${issue.path.join('.')}: ` : '';\n if (issue.code === 'invalid_union') {\n const unionErrors = (issue as ZodIssue & { unionErrors?: ZodError[] }).unionErrors;\n if (unionErrors?.length) {\n const branches = unionErrors\n .map((unionError, i) => {\n const branchIssues = unionError.issues\n .map((nested) => formatZodIssue(nested, depth + 1))\n .join(', ');\n return `option[${i}]: { ${branchIssues} }`;\n })\n .join(' | ');\n return `${path}no matching union member (${branches})`;\n }\n }\n return `${path}${issue.message}`;\n};\n\nconst formatZodError = (error: ZodError): string => {\n return error.issues.map((issue) => formatZodIssue(issue)).join('; ');\n};\n\n/**\n * Validate Error Class for validating params input, this will be used by SDKError class.\n * @ignore\n */\nexport class ValidationError extends Error {\n issues: ZodError['issues'];\n\n constructor(zodError: ZodError) {\n const message = formatZodError(zodError);\n const issuesSummary = JSON.stringify(zodError.issues, null, 2);\n super(`${message}\\nIssues: ${issuesSummary}`);\n this.issues = zodError.issues;\n }\n}\n\n/**\n * @ignore\n * @param params\n * @param config\n */\nexport const validateRequestSchema = <T extends CommonServiceParams>(\n params: T,\n config?: RequestValidationConfig,\n): T => {\n const requestSchema = config?.schema\n ? commonServiceRequestSchema.extend(config.schema.shape)\n : commonServiceRequestSchema;\n\n // Apply all refinements using superRefine for better type compatibility in Zod v4\n const finalSchema = requestSchema.superRefine((data, ctx) => {\n // Validate common params\n if (!('commonBaseURL' in data) && !('customServiceBaseURL' in data)) {\n ctx.addIssue({\n code: 'custom',\n message: 'commonBaseURL or customServiceBaseURL is required',\n });\n }\n\n // Apply optional refinements\n if (config?.refinements?.length) {\n for (const refinement of config.refinements) {\n if (!refinement.check(data as T)) {\n ctx.addIssue({\n code: 'custom',\n message: refinement.message,\n });\n }\n }\n }\n });\n\n const validation = finalSchema.safeParse(params);\n if (!validation.success) {\n throw new ValidationError(validation.error);\n }\n\n return params;\n};\n","import { generateTomTomHeaders, mergeFromGlobal } from '@tomtom-org/maps-sdk/core';\nimport { buildResponseError, buildValidationError } from './errors';\nimport type { ValidationError } from './schema/validation';\nimport { validateRequestSchema } from './schema/validation';\nimport type { CommonServiceParams, ServiceTemplate } from './serviceTypes';\nimport type { ServiceName } from './types/servicesTypes';\n\n/**\n * @ignore\n * Template execution of a service call.\n * Any service goes through the same template steps:\n * 1- Build request\n * 2- Send request and get API response\n * 3- Parse and return API response\n * @param params The parameters for that specific service call.\n * @param template The implementation of the template steps.\n * @param serviceName The name of the service.\n */\nexport const callService = async <PARAMS extends CommonServiceParams, ApiRequest, ApiResponse, RESPONSE>(\n params: PARAMS,\n template: ServiceTemplate<PARAMS, ApiRequest, ApiResponse, RESPONSE>,\n serviceName: ServiceName,\n): Promise<RESPONSE> => {\n const customApiVersion = template.getAPIVersion?.(params);\n const mergedParams = mergeFromGlobal({ ...params, ...(customApiVersion && { apiVersion: customApiVersion }) });\n // (params.validateRequest defaults to true, thus true and undefined are the same)\n if (params.validateRequest === undefined || params.validateRequest) {\n try {\n validateRequestSchema<PARAMS>(mergedParams, template.requestValidation);\n } catch (e) {\n throw buildValidationError(e as ValidationError, serviceName);\n }\n }\n const apiRequest = template.buildRequest(mergedParams);\n const headers = generateTomTomHeaders(mergedParams);\n params.onAPIRequest?.(apiRequest);\n\n try {\n const apiResponse = await template.sendRequest(apiRequest, headers);\n params.onAPIResponse?.(apiRequest, apiResponse);\n return template.parseResponse(await apiResponse.data, mergedParams);\n } catch (e) {\n params.onAPIResponse?.(apiRequest, e);\n throw buildResponseError(e, serviceName, template.parseResponseError);\n }\n};\n","import { isProxyCredentialsMode, RetryConfig, TomTomConfig, type TomTomHeaders } from '@tomtom-org/maps-sdk/core';\nimport type { FetchInput, GetObject, ParsedFetchResponse, PostObject } from './types/fetch';\n\n/**\n * Custom error class for HTTP fetch errors.\n */\nclass FetchError extends Error {\n public readonly status: number;\n public readonly data?: unknown;\n\n constructor(status: number, message?: string, data?: unknown) {\n super(message ?? `HTTP Error ${status}`);\n this.name = 'FetchError';\n this.status = status;\n this.data = data;\n\n // Maintains proper stack trace for where our error was thrown (only available on V8)\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, FetchError);\n }\n }\n}\n\n// Returns the response as a JSON object or throws an error if the response isn't successful.\nconst returnOrThrow = async <T>(response: Response): ParsedFetchResponse<T> => {\n if (response.ok) {\n return { data: await response.json(), status: response.status };\n }\n let message: string | undefined;\n let errorBody;\n const contentType = response.headers.get('content-type');\n if (response.bodyUsed) {\n message = response.statusText;\n } else if (contentType?.includes('application/json')) {\n errorBody = await response.json();\n message = errorBody?.errorText ?? errorBody?.message ?? errorBody?.detailedError?.message;\n } else if (contentType?.includes('text/xml')) {\n errorBody = await response.text();\n message = response.statusText;\n }\n\n throw new FetchError(response.status, message, errorBody);\n};\n\n/**\n * Wraps a fetch call with automatic retry on 429 (Too Many Requests) responses.\n * Respects the Retry-After header when present, otherwise uses exponential backoff.\n * @ignore\n */\nconst fetchWithRetry = async (fetchFn: () => Promise<Response>): Promise<Response> => {\n const retryConfig = TomTomConfig.instance.get().retry;\n if (!retryConfig) {\n return fetchFn();\n }\n\n const { initialWaitMs, backoffFactor, timeoutMs } = retryConfig as Required<RetryConfig>;\n\n const startTime = Date.now();\n let backoffMs = initialWaitMs;\n let response = await fetchFn();\n\n while (response.status === 429) {\n const retryAfterSeconds = Number.parseInt(response.headers.get('retry-after') ?? '', 10);\n const waitMs = Number.isFinite(retryAfterSeconds) ? retryAfterSeconds * 1000 : backoffMs;\n\n if (Date.now() - startTime + waitMs > timeoutMs) {\n break;\n }\n\n // Drain the body to free the connection\n await response.text().catch(() => {});\n\n await new Promise((resolve) => setTimeout(resolve, waitMs));\n backoffMs *= backoffFactor;\n response = await fetchFn();\n }\n\n return response;\n};\n\nconst proxyModeCredentials = (): RequestCredentials | undefined => (isProxyCredentialsMode() ? 'include' : undefined);\n\n/**\n * Fetches the given HTTP JSON resource with an HTTP GET request and returns a promise with the response as a JSON object.\n * If the response isn't successful, it returns a rejected promise with the http error code.\n * @ignore\n * @param input A URL or a GET object with URL and optional service-specific headers.\n * @param sdkHeaders SDK-wide TomTom headers to be sent with the request.\n */\nexport const get = async <T>(input: URL | GetObject, sdkHeaders: TomTomHeaders): ParsedFetchResponse<T> =>\n returnOrThrow(\n await fetchWithRetry(() => {\n const credentials = proxyModeCredentials();\n const { url, headers: inputHeaders } = input instanceof URL ? { url: input, headers: undefined } : input;\n const headers = { ...sdkHeaders, ...inputHeaders };\n return fetch(url, credentials ? { headers, credentials } : { headers });\n }),\n );\n\n/**\n * Fetches the given HTTP JSON resource with an HTTP POST request and returns a promise with the response as a JSON object.\n * If the response isn't successful, it returns a rejected promise with the http error code.\n * @ignore\n * @param input The POST object with URL, optional payload, and optional service-specific headers.\n * @param sdkHeaders SDK-wide TomTom headers to be sent with the request.\n */\nexport const post = async <T, D>(input: PostObject<D>, sdkHeaders: TomTomHeaders): ParsedFetchResponse<T> =>\n returnOrThrow(\n await fetchWithRetry(() => {\n const credentials = proxyModeCredentials();\n const init: RequestInit = {\n method: 'POST',\n body: JSON.stringify(input.data),\n headers: { ...sdkHeaders, ...input.headers, 'Content-Type': 'application/json' },\n };\n if (credentials) init.credentials = credentials;\n\n return fetch(input.url, init);\n }),\n );\n\n/**\n * Fetches the given HTTP JSON resource with the given HTTP operation and URL/Payload as applicable.\n * * Useful for services which can use different HTTP methods depending on the parameters.\n * @param input The input object (e.g. containing either GET or POST data)\n * @param sdkHeaders SDK-wide TomTom headers to be sent with the request.\n * @ignore\n */\nexport const fetchWith = async <T, D = void>(\n input: FetchInput<D>,\n sdkHeaders: TomTomHeaders,\n): ParsedFetchResponse<T> => {\n if (input.method === 'GET') return get<T>(input, sdkHeaders);\n if (input.method === 'POST') return post<T, D>(input, sdkHeaders);\n throw new Error(`Unsupported HTTP method received: ${(input as { method: string }).method}`);\n};\n","import { z } from 'zod';\n\n/**\n * @ignore\n */\nexport const lineStringCoordsSchema = z.array(z.array(z.number()));\n\n/**\n * @ignore\n */\nexport const geometrySchema = z\n .object({\n type: z.enum([\n 'Point',\n 'MultiPoint',\n 'LineString',\n 'MultiLineString',\n 'Polygon',\n 'MultiPolygon',\n 'GeometryCollection',\n 'Circle',\n ]),\n coordinates: z.union([\n z.array(z.number()),\n lineStringCoordsSchema,\n z.array(z.array(z.array(z.number()))),\n z.array(z.array(z.array(z.array(z.number())))),\n ]),\n radius: z.optional(z.number()),\n radiusMeters: z.optional(z.number()),\n bbox: z.optional(z.array(z.number())),\n })\n .check(\n z.refine(\n (data) => (data.type === 'Circle' ? Boolean(data.radius) : true),\n 'type: \"Circle\" must have radius property',\n ),\n );\n\n/**\n * @ignore\n */\nexport const featureSchema = z.object({\n type: z.literal('Feature'),\n geometry: geometrySchema,\n id: z.optional(z.union([z.string(), z.number()])),\n properties: z.unknown().optional(),\n bbox: z.optional(z.array(z.number())),\n});\n\n/**\n * @ignore\n */\nexport const featureCollectionSchema = z.object({\n type: z.literal('FeatureCollection'),\n features: z.array(featureSchema),\n id: z.optional(z.union([z.string(), z.number()])),\n properties: z.unknown().optional(),\n bbox: z.optional(z.array(z.number())),\n});\n\n/**\n * @ignore\n */\nexport const hasLngLatSchema = z.union([\n z.tuple([z.number().min(-180).max(180), z.number().min(-90).max(90)]),\n z.tuple([z.number().min(-180).max(180), z.number().min(-90).max(90), z.number()]),\n z.object({\n type: z.literal('Point'),\n coordinates: z.array(z.number()),\n }),\n featureSchema,\n]);\n\n/**\n * @ignore\n */\nconst geoJsonbBoxSchema = z.union([\n z.array(z.number()).refine((arr) => arr.length === 4, { message: 'BBox must have 4 elements' }),\n z.array(z.number()).refine((arr) => arr.length === 6, { message: 'BBox must have 6 elements' }),\n]);\n\n/**\n * @ignore\n */\nexport const GeoJSONSchema = z.union([geometrySchema, featureSchema, featureCollectionSchema]);\n\n/**\n * @ignore\n */\nexport const hasBBoxSchema = z.union([geoJsonbBoxSchema, GeoJSONSchema, z.array(GeoJSONSchema)]);\n","import { views } from '@tomtom-org/maps-sdk/core';\nimport { z } from 'zod';\nimport { hasLngLatSchema } from './geometriesSchema';\n\n/**\n * @ignore\n */\nexport const commonPlacesParamsSchema = z.object({\n query: z.string().optional(),\n position: hasLngLatSchema.optional(),\n limit: z.number().max(100).optional(),\n extendedPostalCodesFor: z.array(z.string()).optional(),\n mapcodes: z.array(z.string()).optional(),\n view: z.enum(views).optional(),\n geographyTypes: z.array(z.string()).optional(),\n});\n","import { poiCategories } from '@tomtom-org/maps-sdk/core';\nimport { z } from 'zod';\nimport { commonPlacesParamsSchema } from '../shared/schema/commonPlacesParamsSchema';\n\nconst searchExtraParamsOptional = z.object({\n indexes: z.array(z.string()).optional(),\n poiCategories: z.array(z.enum(poiCategories)).optional(),\n poiBrands: z.array(z.string()).optional(),\n connectors: z.array(z.string()).optional(),\n fuelTypes: z.array(z.string()).optional(),\n openingHours: z.string().optional(),\n timeZone: z.string().optional(),\n relatedPois: z.string().optional(),\n minPowerKW: z.number().optional(),\n maxPowerKW: z.number().optional(),\n minFuzzyLevel: z.number().optional(),\n maxFuzzyLevel: z.number().optional(),\n});\n\n/**\n * @ignore\n */\nexport const commonSearchParamsSchema = commonPlacesParamsSchema.extend(searchExtraParamsOptional.shape);\n","import { z } from 'zod';\nimport { commonSearchParamsSchema } from '../search/commonSearchParamsSchema';\nimport { lineStringCoordsSchema } from '../shared/schema/geometriesSchema';\n\nconst lineStringSchema = z.object({\n type: z.literal('LineString'),\n coordinates: lineStringCoordsSchema,\n});\n\nconst routeFeatureSchema = z.object({\n type: z.literal('Feature'),\n geometry: lineStringSchema,\n});\n\nconst positionsArraySchema = lineStringCoordsSchema;\n\nconst routeSchema = z.union([lineStringSchema, routeFeatureSchema, positionsArraySchema]);\n\nconst alongRouteSearchRequestMandatory = z.object({\n route: routeSchema,\n maxDetourTimeSeconds: z.number().int().positive(),\n});\n\nconst alongRouteSearchRequestOptional = z.object({\n sortBy: z.enum(['detourTime', 'detourOffset']).optional(),\n});\n\n/**\n * @ignore\n */\nexport const alongRouteSearchRequestSchema = commonSearchParamsSchema.extend({\n ...alongRouteSearchRequestMandatory.shape,\n ...alongRouteSearchRequestOptional.shape,\n});\n","import type { HasLngLat, POICategory, TomTomAPIHeaders } from '@tomtom-org/maps-sdk/core';\nimport { getPosition, poiCategoriesToID } from '@tomtom-org/maps-sdk/core';\nimport { isNil } from 'lodash-es';\nimport type { CommonServiceParams } from '../serviceTypes';\n\n/**\n * Builds the TomTom API request headers common to every service: the API key,\n * API version, and language. Service request builders spread the result and add\n * any service-specific headers (e.g. `Attributes`) on top.\n * @ignore\n */\nexport const buildCommonServiceRequestHeaders = (params: CommonServiceParams): TomTomAPIHeaders => ({\n ...(params.apiKey && { 'TomTom-Api-Key': params.apiKey }),\n ...(!isNil(params.apiVersion) && { 'TomTom-Api-Version': String(params.apiVersion) }),\n ...(params.language && { 'Accept-Language': params.language }),\n});\n\n/**\n * @ignore\n * @param urlParams\n * @param params\n */\nexport const appendCommonParams = (urlParams: URLSearchParams, params: CommonServiceParams): void => {\n urlParams.append('apiVersion', String(params.apiVersion));\n if (params.apiKey) {\n urlParams.append('key', params.apiKey);\n }\n params.language && urlParams.append('language', params.language);\n};\n\n/**\n * Adds parameter from the array by repeating each array part into a query parameter of the same name.\n * E.g. ...&avoid=motorways&avoid=ferries&...\n * @ignore\n * @param urlParams\n * @param paramName\n * @param paramArray\n */\nexport const appendByRepeatingParamName = (\n urlParams: URLSearchParams,\n paramName: string,\n paramArray?: string[],\n): void => {\n for (const param of paramArray || []) {\n urlParams.append(paramName, param);\n }\n};\n\n/**\n * @ignore\n */\nexport const appendByJoiningParamValue = (\n urlParams: URLSearchParams,\n name: string,\n values?: string[] | number[] | (string | number)[],\n): void => {\n if (Array.isArray(values) && values.length > 0) {\n urlParams.append(name, values.join(','));\n }\n};\n\n/**\n * @ignore\n */\nexport const appendOptionalParam = (\n urlParams: URLSearchParams,\n name: string,\n value?: string | number | boolean,\n): void => {\n !isNil(value) && urlParams.append(name, String(value));\n};\n\n/**\n * Adds lat and lon parameters to the url.\n * @ignore\n * @param urlParams\n * @param hasLngLat\n */\nexport const appendLatLonParamsFromPosition = (urlParams: URLSearchParams, hasLngLat: HasLngLat | undefined): void => {\n const position = getPosition(hasLngLat);\n if (position) {\n urlParams.append('lat', String(position[1]));\n urlParams.append('lon', String(position[0]));\n }\n};\n\n/**\n * Map POICategory values to their numeric IDs for use in API requests.\n * @ignore\n */\nexport const mapPOICategoriesToIDs = (categories: POICategory[]): number[] =>\n categories.map((category) => poiCategoriesToID[category]);\n","import type { AlongRouteSearchParams } from '../../along-route-search';\nimport type { FuzzySearchParams } from '../../fuzzy-search';\nimport type { GeometrySearchParams } from '../../geometry-search';\nimport {\n appendByJoiningParamValue,\n appendCommonParams,\n appendLatLonParamsFromPosition,\n appendOptionalParam,\n mapPOICategoriesToIDs,\n} from './requestBuildingUtils';\n\n/**\n * @ignore\n */\nexport const PLACES_URL_PATH = '/maps/orbis/places';\n\n/**\n * Appends request parameters common to search APIs such as fuzzy + geometry search.\n * * Mutates the given searchURL with the appended parameters.\n * @param searchUrl The search URL to append parameters to. Should come without any parameters at this point.\n * @param params The search parameters, with global configuration already merged into them.\n */\nexport const appendCommonSearchParams = (\n searchUrl: URL,\n params: FuzzySearchParams | GeometrySearchParams | AlongRouteSearchParams,\n): void => {\n const urlParams = searchUrl.searchParams;\n appendCommonParams(urlParams, params);\n appendOptionalParam(urlParams, 'limit', params.limit);\n appendLatLonParamsFromPosition(urlParams, 'position' in params ? params.position : undefined);\n\n appendByJoiningParamValue(urlParams, 'fuelSet', params.fuelTypes);\n appendByJoiningParamValue(urlParams, 'idxSet', params.indexes);\n appendByJoiningParamValue(urlParams, 'brandSet', params.poiBrands);\n params.poiCategories &&\n appendByJoiningParamValue(urlParams, 'categorySet', mapPOICategoriesToIDs(params.poiCategories));\n appendByJoiningParamValue(urlParams, 'connectorSet', params.connectors);\n appendByJoiningParamValue(urlParams, 'mapcodes', params.mapcodes);\n appendByJoiningParamValue(urlParams, 'extendedPostalCodesFor', params.extendedPostalCodesFor);\n\n appendOptionalParam(urlParams, 'minPowerKW', params.minPowerKW);\n appendOptionalParam(urlParams, 'maxPowerKW', params.maxPowerKW);\n appendOptionalParam(urlParams, 'view', params.view);\n appendOptionalParam(urlParams, 'openingHours', params.openingHours);\n appendOptionalParam(urlParams, 'timeZone', params.timeZone);\n appendOptionalParam(urlParams, 'relatedPois', params.relatedPois);\n appendByJoiningParamValue(urlParams, 'entityTypeSet', params.geographyTypes);\n};\n","import type { LineString } from 'geojson';\nimport { appendCommonSearchParams, PLACES_URL_PATH } from '../shared/request/commonSearchRequestBuilder';\nimport { appendOptionalParam } from '../shared/request/requestBuildingUtils';\nimport type { AlongRouteSearchParams, AlongRouteSearchRequestAPI } from './types';\n\nconst toLineString = (route: AlongRouteSearchParams['route']): LineString => {\n if (Array.isArray(route)) {\n return { type: 'LineString', coordinates: route };\n }\n return route.type === 'Feature' ? route.geometry : route;\n};\n\nconst buildUrlBasePath = (params: AlongRouteSearchParams): string =>\n params.customServiceBaseURL ??\n `${params.commonBaseURL}${PLACES_URL_PATH}/searchAlongRoute/${params.query ?? ''}.json`;\n\n/**\n * Default function for building an along-route search request from {@link AlongRouteSearchParams}.\n * @param params The along-route search parameters, with global configuration already merged into them.\n */\nexport const buildAlongRouteSearchRequest = (params: AlongRouteSearchParams): AlongRouteSearchRequestAPI => {\n const url = new URL(buildUrlBasePath(params));\n appendCommonSearchParams(url, params);\n const urlParams = url.searchParams;\n urlParams.append('maxDetourTime', String(params.maxDetourTimeSeconds));\n appendOptionalParam(urlParams, 'sortBy', params.sortBy);\n appendOptionalParam(urlParams, 'spreadingMode', 'plan');\n\n return {\n url,\n data: {\n route: {\n points: toLineString(params.route).coordinates.map((coord) => ({\n lat: coord[1],\n lon: coord[0],\n })),\n },\n },\n };\n};\n","import type {\n ChargingPointAvailability,\n ChargingPointStatus,\n ChargingStation,\n Connector,\n ConnectorAvailability,\n ConnectorCount,\n} from '@tomtom-org/maps-sdk/core';\n\n/**\n * @ignore\n */\nexport const toChargingPointAvailability = (chargingStations: ChargingStation[]): ChargingPointAvailability => {\n const availability: ChargingPointAvailability = { count: 0, statusCounts: {} };\n for (const station of chargingStations) {\n for (const chargingPoint of station.chargingPoints) {\n availability.count++;\n availability.statusCounts[chargingPoint.status] =\n (availability.statusCounts[chargingPoint.status] || 0) + 1;\n }\n }\n return availability;\n};\n\n// Two connectors can be considered equal when they have the same type and power:\nconst areEqual = (connectorA: Connector, connectorB: Connector) =>\n connectorA.type === connectorB.type && connectorA.ratedPowerKW === connectorB.ratedPowerKW;\n\nconst addConnectorCount = (\n connectors: Connector[] | undefined,\n counts: ConnectorCount[], // we are mutating this input\n): void => {\n if (!connectors) {\n // defensive check, sometimes connectors are undefined\n return;\n }\n for (const connector of connectors) {\n const existingCount = counts.find((connectorCount) => areEqual(connector, connectorCount.connector));\n if (existingCount) {\n existingCount.count++;\n } else {\n // new count entry:\n counts.push({ connector, count: 1 });\n }\n }\n};\n\nconst addConnectorCountAndStatus = (\n connectors: Connector[] | undefined,\n status: ChargingPointStatus | undefined,\n availabilities: ConnectorAvailability[], // we are mutating this input\n): void => {\n if (!connectors) {\n // defensive check, sometimes connectors are undefined\n return;\n }\n for (const connector of connectors) {\n const existingAvailability = availabilities.find((connectorAvailability) =>\n areEqual(connector, connectorAvailability.connector),\n );\n if (existingAvailability) {\n existingAvailability.count++;\n if (status) {\n // we're mutating the input object here:\n const statusCounts = existingAvailability.statusCounts;\n existingAvailability.statusCounts[status] = (statusCounts[status] || 0) + 1;\n }\n } else {\n // new availability entry:\n availabilities.push({ connector, count: 1, statusCounts: status ? { [status]: 1 } : {} });\n }\n }\n};\n\n/**\n * @ignore\n */\nexport const toConnectorCounts = (connectors: Connector[]): ConnectorCount[] => {\n const availabilities: ConnectorAvailability[] = [];\n addConnectorCount(connectors, availabilities);\n return availabilities;\n};\n\n/**\n * @ignore\n */\nexport const toConnectorBasedAvailabilities = (chargingStations: ChargingStation[]): ConnectorAvailability[] => {\n const availabilities: ConnectorAvailability[] = [];\n for (const station of chargingStations) {\n for (const chargingPoint of station.chargingPoints) {\n addConnectorCountAndStatus(chargingPoint.connectors, chargingPoint.status, availabilities);\n }\n }\n return availabilities;\n};\n","import { ChargingSpeed } from '@tomtom-org/maps-sdk/core';\n\n/**\n * @ignore\n */\nexport const toChargingSpeed = (powerInKW: number): ChargingSpeed => {\n if (powerInKW < 12) {\n return 'slow';\n } else if (powerInKW < 50) {\n return 'regular';\n } else if (powerInKW < 150) {\n return 'fast';\n }\n return 'ultra-fast';\n};\n","import type { BBox } from '@tomtom-org/maps-sdk/core';\nimport type { Position } from 'geojson';\nimport type { BoundingBoxAPI, BoundingBoxTopLeftAPI, LatLonAPI } from './types/apiPlacesResponseTypes';\n\n/**\n * @ignore\n * @param csv\n */\nexport const csvLatLngToPosition = (csv: string): Position => {\n const splitLatLng = csv.split(',');\n return [Number(splitLatLng[1]), Number(splitLatLng[0])];\n};\n\n/**\n * @ignore\n * @param position\n */\nexport const positionToCSVLatLon = (position: Position): string => `${position[1]},${position[0]}`;\n\nconst hasTopLeftPoint = (bbox: BoundingBoxAPI): bbox is BoundingBoxTopLeftAPI => {\n return (<BoundingBoxTopLeftAPI>bbox).topLeftPoint !== undefined;\n};\n\n/**\n * @ignore\n * @param apiBBox\n */\nexport const apiToGeoJSONBBox = (apiBBox: BoundingBoxAPI): BBox => {\n let westSouth: Position;\n let eastNorth: Position;\n if (hasTopLeftPoint(apiBBox)) {\n westSouth = [apiBBox.topLeftPoint.lon, apiBBox.btmRightPoint.lat];\n eastNorth = [apiBBox.btmRightPoint.lon, apiBBox.topLeftPoint.lat];\n } else {\n westSouth = csvLatLngToPosition(apiBBox.southWest);\n eastNorth = csvLatLngToPosition(apiBBox.northEast);\n }\n return [westSouth[0], westSouth[1], eastNorth[0], eastNorth[1]];\n};\n\n/**\n * @ignore\n * @param point\n */\nexport const latLonAPIToPosition = (point: LatLonAPI): Position => {\n return [point.lon, point.lat];\n};\n","import type { Moment, OpeningHours, Place, POICategory, SearchPlaceProps, TimeRange } from '@tomtom-org/maps-sdk/core';\nimport { poiIDsToCategories, toPointGeometry } from '@tomtom-org/maps-sdk/core';\nimport { omit } from 'lodash-es';\nimport { toConnectorCounts } from '../ev-charging-stations-availability/connectorAvailability';\nimport { toChargingSpeed } from './ev';\nimport { apiToGeoJSONBBox, latLonAPIToPosition } from './geometry';\nimport type {\n BrandAPI,\n CategoryAPI,\n CommonSearchPlaceResultAPI,\n MomentAPI,\n OpeningHoursAPI,\n SummaryAPI,\n} from './types/apiPlacesResponseTypes';\nimport type { SearchSummary } from './types/searchSummary';\n\nconst parseYyyymmddDate = (dateYyyymmdd: string): { year: number; month: number; day: number } => {\n const splitDate = dateYyyymmdd.split('-');\n return {\n year: Number.parseInt(splitDate[0]),\n month: Number.parseInt(splitDate[1]),\n day: Number.parseInt(splitDate[2]),\n };\n};\n\nconst parseMoment = (momentApi: MomentAPI): Moment => {\n const { year, month, day } = parseYyyymmddDate(momentApi.date);\n return {\n dateYYYYMMDD: momentApi.date,\n year,\n month,\n day,\n hour: momentApi.hour,\n minute: momentApi.minute,\n date: new Date(year, month - 1, day, momentApi.hour, momentApi.minute),\n };\n};\n\nconst alwaysOpenInThisPeriod = (timeRanges: TimeRange[]): boolean =>\n timeRanges.length === 1 && timeRanges[0].start.hour === 0 && timeRanges[0].end.hour === 0;\n\n/**\n * @ignore\n */\nexport const parseOpeningHours = (openingHoursApi: OpeningHoursAPI): OpeningHours => {\n const timeRanges = openingHoursApi.timeRanges.map(\n (timeRangeApi): TimeRange => ({\n start: parseMoment(timeRangeApi.startTime),\n end: parseMoment(timeRangeApi.endTime),\n }),\n );\n return {\n mode: openingHoursApi.mode,\n timeRanges,\n alwaysOpenThisPeriod: alwaysOpenInThisPeriod(timeRanges),\n };\n};\n\n/**\n * Shared response parsing between geometry search and place by id service.\n * @ignore\n */\nexport const parseSearchAPIResult = (result: CommonSearchPlaceResultAPI): Place<SearchPlaceProps> => {\n const { position, entryPoints, poi, id, dist, boundingBox, chargingPark, ...rest } = result;\n const connectors = chargingPark?.connectors?.map((connector) => ({\n ...omit(connector, 'connectorType'),\n type: connector.connectorType,\n chargingSpeed: toChargingSpeed(connector.ratedPowerKW),\n }));\n return {\n type: 'Feature',\n geometry: toPointGeometry(latLonAPIToPosition(position)),\n ...(boundingBox && { bbox: apiToGeoJSONBBox(boundingBox) }),\n id,\n properties: {\n ...omit(rest, 'viewport'),\n ...(dist && { distance: dist }),\n ...(entryPoints?.length && {\n entryPoints: entryPoints.map((entrypoint) => ({\n ...entrypoint,\n position: latLonAPIToPosition(entrypoint.position),\n })),\n }),\n ...(connectors?.length && {\n chargingPark: {\n ...chargingPark,\n connectors: toConnectorCounts(connectors),\n },\n }),\n ...(poi && {\n poi: {\n name: poi.name,\n categories: (\n poi.categorySet?.map((c: CategoryAPI): POICategory => poiIDsToCategories[c.id]) ?? []\n ).filter((c: POICategory) => !!c),\n localizedCategories: poi.categories ?? [],\n ...(poi.phone && { phone: poi.phone }),\n ...(poi.brands && { brands: poi.brands.map((brand: BrandAPI) => brand.name) }),\n ...(poi.url && { url: poi.url }),\n ...(poi.openingHours && { openingHours: parseOpeningHours(poi.openingHours) }),\n ...(poi.timeZone && { timeZone: poi.timeZone }),\n },\n }),\n },\n };\n};\n\n/**\n * @ignore\n */\nexport const parseSummaryAPI = (summary: SummaryAPI): SearchSummary => {\n const { geoBias, ...rest } = summary;\n\n return {\n ...(geoBias && { geoBias: latLonAPIToPosition(geoBias) }),\n ...rest,\n };\n};\n","import { bboxFromGeoJSON, bboxOnlyIfWithArea } from '@tomtom-org/maps-sdk/core';\nimport { parseSearchAPIResult, parseSummaryAPI } from '../shared/searchResultParsing';\nimport type { AlongRouteSearchResponse, AlongRouteSearchResponseAPI } from './types';\n\n/**\n * Default function to parse an along-route search response.\n * @param apiResponse The API response.\n */\nexport const parseAlongRouteSearchResponse = (apiResponse: AlongRouteSearchResponseAPI): AlongRouteSearchResponse => {\n const features = apiResponse.results.map(parseSearchAPIResult);\n const bbox = bboxOnlyIfWithArea(bboxFromGeoJSON(features));\n return {\n type: 'FeatureCollection',\n properties: {\n ...parseSummaryAPI(apiResponse.summary),\n },\n features,\n ...(bbox && { bbox }),\n };\n};\n","import type { ServiceTemplate } from '../shared';\nimport { post } from '../shared/fetch';\nimport { alongRouteSearchRequestSchema } from './alongRouteSearchRequestSchema';\nimport { buildAlongRouteSearchRequest } from './requestBuilder';\nimport { parseAlongRouteSearchResponse } from './responseParser';\nimport type {\n AlongRouteSearchParams,\n AlongRouteSearchRequestAPI,\n AlongRouteSearchResponse,\n AlongRouteSearchResponseAPI,\n} from './types';\n\n/**\n * Along-route search service template type.\n * @ignore\n */\nexport type AlongRouteSearchTemplate = ServiceTemplate<\n AlongRouteSearchParams,\n AlongRouteSearchRequestAPI,\n AlongRouteSearchResponseAPI,\n AlongRouteSearchResponse\n>;\n\n/**\n * Along-route search service template main implementation.\n * @ignore\n */\nexport const alongRouteSearchTemplate: AlongRouteSearchTemplate = {\n requestValidation: { schema: alongRouteSearchRequestSchema },\n buildRequest: buildAlongRouteSearchRequest,\n sendRequest: post,\n parseResponse: parseAlongRouteSearchResponse,\n};\n","import { callService } from '../shared/serviceTemplate';\nimport type { AlongRouteSearchTemplate } from './alongRouteSearchTemplate';\nimport { alongRouteSearchTemplate } from './alongRouteSearchTemplate';\nimport type { AlongRouteSearchParams, AlongRouteSearchResponse } from './types';\n\n/**\n * Search for places along a route, ranked by detour cost.\n *\n * Returns POIs reachable within a given detour budget from the provided route geometry.\n * Results are sorted by detour time (or offset along the route) so the most\n * accessible stops appear first.\n *\n * @remarks\n * This service is ideal for trip planning use cases:\n * - Find fuel or EV charging stations along the way\n * - Locate food stops or rest areas near the route\n * - Surface services reachable within a time budget\n *\n * The `route` is typically sourced from a {@link calculateRoute} result.\n *\n * @param params Along-route search parameters including the route geometry and detour budget\n * @param customTemplate Advanced customization for request/response handling\n *\n * @returns Promise resolving to a collection of matching places sorted by detour cost\n *\n * @example\n * ```typescript\n * // Find coffee shops within a 5-minute detour\n * const results = await alongRouteSearch({\n * route: routeResult.features[0].geometry,\n * maxDetourTimeSeconds: 300,\n * query: 'coffee',\n * });\n *\n * // EV chargers with a 10-minute budget, sorted by position on route\n * const chargers = await alongRouteSearch({\n * route: routeResult.features[0].geometry,\n * maxDetourTimeSeconds: 600,\n * poiCategories: ['ELECTRIC_VEHICLE_STATION'],\n * sortBy: 'detourOffset',\n * });\n * ```\n *\n * @see [Along Route Search API Documentation](https://docs.tomtom.com/search-api/documentation/tomtom-orbis-maps/search-service/along-route-search)\n *\n * @ignore (exposed via 'search')\n */\nexport const alongRouteSearch = async (\n params: AlongRouteSearchParams,\n customTemplate?: Partial<AlongRouteSearchTemplate>,\n): Promise<AlongRouteSearchResponse> =>\n callService(params, { ...alongRouteSearchTemplate, ...customTemplate }, 'AlongRouteSearch');\n","import { z } from 'zod';\nimport { hasLngLatSchema } from '../shared/schema/geometriesSchema';\n\nconst autocompleteSearchRequestMandatory = z.object({\n query: z.string(),\n});\n\nconst autocompleteSearchRequestOptional = z.object({\n position: hasLngLatSchema.optional(),\n limit: z.number().max(100).optional(),\n radiusMeters: z.number().optional(),\n countries: z.array(z.string()).optional(),\n resultType: z.array(z.string()).optional(),\n});\n\n/**\n * @ignore\n */\nexport const autocompleteSearchRequestSchema = autocompleteSearchRequestMandatory.extend(\n autocompleteSearchRequestOptional.shape,\n);\n","import { PLACES_URL_PATH } from '../shared/request/commonSearchRequestBuilder';\nimport {\n appendByJoiningParamValue,\n appendCommonParams,\n appendLatLonParamsFromPosition,\n appendOptionalParam,\n} from '../shared/request/requestBuildingUtils';\nimport type { AutocompleteSearchParams } from './types';\n\nconst buildUrlBasePath = (mergedOptions: AutocompleteSearchParams): string =>\n mergedOptions.customServiceBaseURL ||\n `${mergedOptions.commonBaseURL}${PLACES_URL_PATH}/autocomplete/${mergedOptions.query}.json`;\n\n/**\n * Default function for building autocomplete request from {@link AutocompleteSearchParams}\n * @param params The autocomplete parameters, with global configuration already merged into them.\n */\nexport const buildAutocompleteSearchRequest = (params: AutocompleteSearchParams): URL => {\n const url = new URL(`${buildUrlBasePath(params)}`);\n const urlParams = url.searchParams;\n /**\n * Auto-complete service defaults the language to en-GB if not specified explicitly as service param\n * Or global config\n */\n params.language = params.language ?? 'en-GB';\n appendCommonParams(urlParams, params);\n appendOptionalParam(urlParams, 'limit', params.limit);\n appendLatLonParamsFromPosition(urlParams, params.position);\n appendByJoiningParamValue(urlParams, 'countrySet', params.countries);\n appendOptionalParam(urlParams, 'radius', params.radiusMeters);\n appendByJoiningParamValue(urlParams, 'resultSet', params.resultType);\n\n return url;\n};\n","import { poiIDsToCategories } from '@tomtom-org/maps-sdk/core';\nimport { latLonAPIToPosition } from '../shared/geometry';\nimport type {\n AutocompleteSearchCategorySegmentAPI,\n AutocompleteSearchResponse,\n AutocompleteSearchResponseAPI,\n AutocompleteSearchResultAPI,\n} from './types';\n\nconst isCategorySegment = (\n segment: AutocompleteSearchResultAPI['segments'][number],\n): segment is AutocompleteSearchCategorySegmentAPI => segment.type === 'category';\n\nconst parseResults = (results: AutocompleteSearchResultAPI[]): AutocompleteSearchResponse['results'] =>\n results.map((result) => ({\n segments: result.segments.map((segment) => {\n if (isCategorySegment(segment)) {\n const { id, ...rest } = segment;\n return { ...rest, category: poiIDsToCategories[Number(id)] };\n }\n return segment;\n }),\n }));\n\n/**\n * Default function to parse autocomplete response.\n * @param apiResponse The API response.\n */\nexport const parseAutocompleteSearchResponse = (\n apiResponse: AutocompleteSearchResponseAPI,\n): AutocompleteSearchResponse => {\n const { position, ...geoBias } = apiResponse.context.geoBias || {};\n return {\n context: {\n ...apiResponse.context,\n geoBias: {\n ...(position && { position: latLonAPIToPosition(position) }),\n radiusMeters: geoBias.radius,\n },\n },\n results: parseResults(apiResponse.results),\n };\n};\n","import type { ServiceTemplate } from '../shared';\nimport { get } from '../shared/fetch';\nimport { autocompleteSearchRequestSchema } from './autocompleteSearchRequestSchema';\nimport { buildAutocompleteSearchRequest } from './requestBuilder';\nimport { parseAutocompleteSearchResponse } from './responseParser';\nimport type { AutocompleteSearchParams, AutocompleteSearchResponse, AutocompleteSearchResponseAPI } from './types';\n\n/**\n * Autocomplete service template type.\n * @ignore\n */\nexport type AutocompleteSearchTemplate = ServiceTemplate<\n AutocompleteSearchParams,\n URL,\n AutocompleteSearchResponseAPI,\n AutocompleteSearchResponse\n>;\n\n/**\n * Autocomplete service template main implementation.\n * @ignore\n */\nexport const autocompleteSearchTemplate: AutocompleteSearchTemplate = {\n requestValidation: { schema: autocompleteSearchRequestSchema },\n buildRequest: buildAutocompleteSearchRequest,\n sendRequest: get,\n parseResponse: parseAutocompleteSearchResponse,\n};\n","import { callService } from '../shared/serviceTemplate';\nimport type { AutocompleteSearchTemplate } from './autocompleteSearchTemplate';\nimport { autocompleteSearchTemplate } from './autocompleteSearchTemplate';\nimport type { AutocompleteSearchParams, AutocompleteSearchResponse } from './types';\n\n/**\n * Autocomplete search queries as the user types, enabling faster and more accurate search.\n *\n * The Autocomplete service recognizes entities (places, addresses, POIs) within a partial\n * input query and offers them as completion suggestions. This enables real-time search\n * assistance and improves the search experience.\n *\n * @remarks\n * Key features:\n * - **Real-time suggestions**: Returns results as the user types\n * - **Entity recognition**: Identifies addresses, POIs, and geographic areas\n * - **Structured results**: Provides both plain text and structured data\n * - **Query refinement**: Helps users formulate more accurate search queries\n * - **Fast response**: Optimized for low-latency interactive use\n *\n * Typical use cases:\n * - Search box autocomplete dropdowns\n * - Address entry forms\n * - Location pickers\n * - Navigation apps\n *\n * @param params - Autocomplete parameters including the partial query\n * @param customTemplate - Advanced customization for request/response handling\n *\n * @returns Promise resolving to autocomplete suggestions\n *\n * @example\n * ```typescript\n * // Autocomplete as user types \"amster\"\n * const suggestions = await autocompleteSearch({\n * query: 'amster',\n * limit: 5\n * });\n * // Returns: Amsterdam, Amsterdam Centraal, etc.\n *\n * // Autocomplete with position bias\n * const localSuggestions = await autocompleteSearch({\n * query: 'main st',\n * position: [4.9041, 52.3676], // Near Amsterdam\n * limit: 10\n * });\n *\n * // Autocomplete restricted to a country with radius\n * const nlSuggestions = await autocompleteSearch({\n * query: 'pizz',\n * countries: ['NL'],\n * position: [4.9041, 52.3676],\n * radiusMeters: 5000\n * });\n * ```\n *\n * @see [Autocomplete API Documentation](https://docs.tomtom.com/search-api/documentation/autocomplete-service/autocomplete)\n * @see [Places Quickstart Guide](https://docs.tomtom.com/maps-sdk-js/guides/services/places/quickstart)\n * @see [Search Guide](https://docs.tomtom.com/maps-sdk-js/guides/services/places/search)\n *\n * @group Autocomplete Search\n */\nexport const autocompleteSearch = async (\n params: AutocompleteSearchParams,\n customTemplate?: Partial<AutocompleteSearchTemplate>,\n): Promise<AutocompleteSearchResponse> =>\n callService(params, { ...autocompleteSearchTemplate, ...customTemplate }, 'Autocomplete');\n","import { alongRouteSearch } from './alongRouteSearch';\nimport type { AlongRouteSearchTemplate } from './alongRouteSearchTemplate';\nimport { alongRouteSearchTemplate } from './alongRouteSearchTemplate';\nimport { buildAlongRouteSearchRequest } from './requestBuilder';\nimport { parseAlongRouteSearchResponse } from './responseParser';\n\nconst customize: {\n alongRouteSearch: typeof alongRouteSearch;\n buildAlongRouteSearchRequest: typeof buildAlongRouteSearchRequest;\n parseAlongRouteSearchResponse: typeof parseAlongRouteSearchResponse;\n alongRouteSearchTemplate: AlongRouteSearchTemplate;\n} = {\n alongRouteSearch,\n buildAlongRouteSearchRequest,\n parseAlongRouteSearchResponse,\n alongRouteSearchTemplate,\n};\nexport default customize;\n","import { autocompleteSearch } from './autocompleteSearch';\nimport type { AutocompleteSearchTemplate } from './autocompleteSearchTemplate';\nimport { autocompleteSearchTemplate } from './autocompleteSearchTemplate';\nimport { buildAutocompleteSearchRequest } from './requestBuilder';\nimport { parseAutocompleteSearchResponse } from './responseParser';\n\nconst customize: {\n autocompleteSearch: typeof autocompleteSearch;\n buildAutocompleteSearchRequest: typeof buildAutocompleteSearchRequest;\n parseAutocompleteSearchResponse: typeof parseAutocompleteSearchResponse;\n autocompleteSearchTemplate: AutocompleteSearchTemplate;\n} = {\n autocompleteSearch,\n buildAutocompleteSearchRequest,\n parseAutocompleteSearchResponse,\n autocompleteSearchTemplate,\n};\nexport default customize;\n","import { z } from 'zod';\nimport { commonServiceRequestSchema } from '../shared/schema/commonParamsSchema';\n\n/**\n * @ignore\n */\nexport const evChargingStationsAvailabilityRequestSchema = commonServiceRequestSchema.extend({\n id: z.string(),\n});\n","import type { Avoidable, HasBBox, TravelMode } from '@tomtom-org/maps-sdk/core';\nimport { VehicleParameters } from './vehicleParams';\n\n/**\n * Available route types, where each type specifies the type of optimization used when calculating routes:\n * * **fast**: Route calculation is optimized by travel time, while keeping the routes sensible. For example, the calculation may avoid shortcuts along inconvenient side roads or long detours that only save very little time.\n * * **short**: Route calculation is optimized such that a good compromise between small travel time and short travel distance is achieved.\n * * **efficient**: Route calculation is optimized such that a good compromise between small travel time and low fuel or energy consumption is achieved.\n * * **thrilling**: Route calculation is optimized such that routes include interesting or challenging roads and use as few motorways as possible.\n * There is a limit of 900km on routes planned with routeType=thrilling.\n *\n * @group Routing\n */\nexport const routeTypes = ['fast', 'short', 'efficient', 'thrilling'] as const;\n\n/**\n * Route optimization strategy for route calculation.\n *\n * Determines what the routing engine optimizes for when calculating the route.\n * Each type produces different routes suited to different use cases.\n *\n * @remarks\n * **Route Type Strategies:**\n *\n * - **`fast`**: Minimize travel time while maintaining practicality\n * - Prefers major roads and highways\n * - Avoids unnecessary detours and shortcuts on minor roads\n * - Best for most everyday use cases (commuting, business travel)\n *\n * - **`short`**: Balance between time and distance\n * - Good compromise between speed and mileage\n * - May use smaller roads to save distance\n * - Useful for short trips or when fuel costs matter\n *\n * - **`efficient`**: Minimize fuel or energy consumption\n * - Optimizes for least energy use\n * - Considers vehicle consumption model\n * - Avoids rapid acceleration/deceleration\n * - Best used with vehicle consumption parameters\n *\n * - **`thrilling`**: Scenic and engaging routes\n * - Prefers curvy, interesting roads\n * - Minimizes motorway usage\n * - **Limited to 900km maximum route length**\n * - Ideal for motorcycle rides or scenic drives\n *\n * @example\n * ```typescript\n * // Fastest route for commuting\n * const routeType: RouteType = 'fast';\n *\n * // Most fuel-efficient route for long trip\n * const ecoRoute: RouteType = 'efficient';\n *\n * // Scenic route for leisure\n * const scenicRoute: RouteType = 'thrilling';\n * ```\n *\n * @group Routing\n */\nexport type RouteType = (typeof routeTypes)[number];\n\n/**\n * Traffic consideration mode for route calculation.\n *\n * Controls how traffic conditions are factored into routing and travel time estimates.\n *\n * @remarks\n * **Traffic Modes:**\n *\n * - **`live`**: Real-time + historical traffic\n * - Includes current traffic jams and incidents\n * - Considers short-term and long-term road closures\n * - Most accurate for immediate departures\n * - Updates with current conditions\n *\n * - **`historical`**: Typical traffic patterns only\n * - Based on historical data for time of day/week\n * - Ignores current traffic conditions\n * - Good for future trip planning\n * - More predictable for scheduled departures\n *\n * @example\n * ```typescript\n * // Route considering current traffic (departing now)\n * const trafficMode: TrafficInput = 'live';\n *\n * // Route based on typical patterns (planning ahead)\n * const plannedRoute: TrafficInput = 'historical';\n * ```\n *\n * @group Routing\n */\nexport type TrafficInput = 'live' | 'historical';\n\n/**\n * Cost model criteria for route optimization.\n *\n * Defines routing preferences and constraints that influence path selection.\n * Combines route type, traffic consideration, and avoidance criteria to determine\n * what makes a route \"better\" in the eyes of the routing engine.\n *\n * @remarks\n * The cost model affects how the route path is calculated, but does not affect what other data the response includes.\n *\n * The cost model balances multiple factors:\n * - Time efficiency\n * - Distance\n * - Fuel/energy consumption\n * - User preferences (avoid tolls, ferries, etc.)\n * - Traffic conditions\n *\n * @example\n * ```typescript\n * // Fast route avoiding tolls\n * const costModel: CostModel = {\n * routeType: 'fast',\n * traffic: 'live',\n * avoid: ['tollRoads']\n * };\n *\n * // Eco-friendly route avoiding highways\n * const ecoCostModel: CostModel = {\n * routeType: 'efficient',\n * traffic: 'historical',\n * avoid: ['motorways', 'ferries']\n * };\n *\n * // Scenic route for leisure\n * const scenicCostModel: CostModel = {\n * routeType: 'thrilling',\n * avoid: ['motorways', 'tollRoads']\n * };\n * ```\n *\n * @group Routing\n */\nexport type CostModel = {\n /**\n * Specifies something that the route calculation should try to avoid when determining the route.\n * @default None\n */\n avoid?: Avoidable[];\n\n /**\n * Rectangular areas for the routing engine to bypass, each expressed as a {@link HasBBox}.\n *\n * Up to 10 rectangles are supported. Useful for avoiding construction zones,\n * restricted areas, or known congestion points.\n *\n * @remarks\n * Constraints per rectangle:\n * - Maximum size: ~160×160 km\n * - Cannot cross the 180th meridian\n * - Latitude must be between −80° and +80°\n *\n * @example\n * ```typescript\n * // Avoid an area using a BBox array [west, south, east, north]\n * avoidAreas: [\n * [2.265938, 48.81851, 2.41115, 48.90309]\n * ]\n * ```\n */\n avoidAreas?: HasBBox[];\n\n /**\n * Decides how traffic is considered for computing routes.\n *\n * Possible values are:\n * * live: In addition to historical travel times, routing and estimated travel time\n * consider traffic jams and short- and long-term closures during the travel time window.\n * * historical: Routing and estimated travel time consider historical travel times and long term closures.\n * Traffic jams and short-term closures during the travel time window do not influence routing or travel time.\n *\n * @default live\n *\n * @remarks\n * This setting does not affect whether live traffic is included in the response or not.\n * * Considering historical traffic in cost model does not mean live traffic is not included in the response.\n */\n traffic?: TrafficInput;\n\n /**\n * Specifies the type of optimization used when calculating routes.\n * Possible values are:\n *\n * * **fast**: Route calculation is optimized by travel time, while keeping the routes sensible. For example, the calculation may avoid shortcuts along inconvenient side roads or long detours that only save very little time.\n * * **short**: Route calculation is optimized such that a good compromise between small travel time and short travel distance is achieved.\n * * **efficient**: Route calculation is optimized such that a good compromise between small travel time and low fuel or energy consumption is achieved.\n * * **thrilling**: Route calculation is optimized such that routes include interesting or challenging roads and use as few motorways as possible.\n * There is a limit of 900km on routes planned with routeType=thrilling.\n * @default fast\n */\n routeType?: RouteType;\n};\n\ntype DepartArriveOption = 'departAt' | 'arriveBy';\n\n/**\n * Departure or arrival time specification for route planning.\n *\n * Allows specifying either when to depart from the origin or when to arrive at the destination.\n * The routing engine calculates the route optimized for that specific time window, considering\n * traffic patterns for that time of day.\n *\n * @typeParam Option - Whether this specifies departure or arrival time\n *\n * @remarks\n * Traffic conditions vary significantly by:\n * - Time of day (rush hour vs off-peak)\n * - Day of week (weekday vs weekend)\n * - Special events or holidays\n *\n * Specifying a departure or arrival time enables the router to:\n * - Use appropriate traffic data for that time\n * - Plan around rush hour or quiet periods\n * - Calculate accurate arrival/departure times\n * - Account for time-dependent road restrictions\n *\n * **Important Notes:**\n * - Times are processed in the timezone of the origin/destination\n *\n * @example\n * ```typescript\n * // Depart at specific time (morning commute)\n * const departParams: DepartArriveParams = {\n * option: 'departAt',\n * date: new Date('2025-10-20T08:00:00Z')\n * };\n *\n * // Arrive by specific time (catch a flight)\n * const arriveParams: DepartArriveParams = {\n * option: 'arriveBy',\n * date: new Date('2025-10-20T14:00:00Z')\n * };\n *\n * // Plan route avoiding rush hour\n * const offPeakDepart: DepartArriveParams = {\n * option: 'departAt',\n * date: new Date('2025-10-20T10:30:00Z') // After morning rush\n * };\n * ```\n *\n * @group Routing\n */\nexport type DepartArriveParams<Option extends DepartArriveOption = DepartArriveOption> = {\n /**\n * Whether to specify a departure or arrival time.\n *\n * @remarks\n * - `departAt`: Calculate route from this departure time forward\n * - `arriveBy`: Calculate route backward to arrive by this time\n */\n option: Option;\n\n /**\n * The date and time to depart or arrive.\n *\n * @remarks\n * If past dates are supplied or dates that are impossible to achieve\n * (e.g., an imminent arrival date for a very long route), the system\n * will default to departing immediately.\n *\n * Times should be specified in ISO 8601 format or as JavaScript Date objects.\n */\n date: Date;\n};\n\n/**\n * Common parameters shared across all routing service requests.\n *\n * These parameters configure how routes are calculated, including optimization\n * strategy, vehicle characteristics, timing constraints, and travel preferences.\n * They provide a consistent interface across different routing services.\n *\n * @remarks\n * Most routing services (calculateRoute, calculateMatrixRoute, calculateReachableRange)\n * accept these parameters to customize route calculation. They control:\n * - What to optimize for (time, distance, fuel)\n * - Vehicle constraints and capabilities\n * - When to travel (affecting traffic)\n * - What features to avoid\n * - Mode of transportation\n *\n * **Service Compatibility:**\n * - {@link calculateRoute}: All parameters supported\n * - {@link calculateMatrixRoute}: Subset of parameters\n * - {@link calculateReachableRange}: Subset of parameters\n *\n * @example\n * ```typescript\n * // Standard car route avoiding tolls\n * const routingParams: CommonRoutingParams = {\n * costModel: {\n * routeType: 'fast',\n * traffic: 'live',\n * avoid: ['tollRoads']\n * },\n * travelMode: 'car',\n * when: {\n * option: 'departAt',\n * date: new Date('2025-10-20T08:00:00Z')\n * }\n * };\n *\n * // Electric vehicle route with consumption model\n * const evRoutingParams: CommonRoutingParams = {\n * costModel: {\n * routeType: 'efficient',\n * traffic: 'live'\n * },\n * travelMode: 'car',\n * vehicle: {\n * engineType: 'electric',\n * model: {\n * dimensions: {\n * weightKG: 2000\n * },\n * engine: {\n * consumption: {\n * speedToConsumption: [\n * { speedKMH: 50, consumptionUnitsPer100KM: 15 },\n * { speedKMH: 90, consumptionUnitsPer100KM: 18 },\n * { speedKMH: 120, consumptionUnitsPer100KM: 22 }\n * ]\n * }\n * }\n * },\n * state: {\n * currentChargeInkWh: 60\n * }\n * }\n * };\n *\n * // Truck route with restrictions\n * const truckParams: CommonRoutingParams = {\n * costModel: {\n * routeType: 'short',\n * avoid: ['tollRoads', 'ferries']\n * },\n * travelMode: 'truck',\n * vehicle: {\n * model: {\n * dimensions: {\n * weightKG: 40000\n * }\n * },\n * restrictions: {\n * maxSpeedKMH: 90\n * }\n * }\n * };\n * ```\n *\n * @see [Common Routing Parameters Documentation](https://docs.tomtom.com/routing-api/documentation/routing/common-routing-parameters)\n *\n * @group Routing\n */\nexport type CommonRoutingParams = {\n /**\n * Criteria that specifies what paths to prefer during routing.\n */\n costModel?: CostModel;\n\n /**\n * The primary means of transportation to be used while routing.\n *\n * The travel mode for the requested route.\n * Note that the requested travelMode may not be available for the entire route. Where\n * the requested travelMode is not available for a particular section, the element of the\n * response for that section will be 'other'.\n * @default None\n */\n travelMode?: TravelMode;\n\n /**\n * Specifies when to depart or arrive.\n * If past dates are supplied or in a way that are impossible to achieve\n * (e.g. an imminent arrival date for a long route), then it will default to departing now.\n * @default depart now\n */\n when?: DepartArriveParams;\n\n /**\n * Parameters for the vehicle that will be used to drive the route.\n */\n vehicle?: VehicleParameters;\n\n /**\n * The version of the API to use.\n * * The SDK will use the right default when not specified.\n * * Use it only if you really need to target a specific API version.\n */\n apiVersion?: number;\n};\n","/**\n * Whether the vehicle carries an electronic toll collection transponder.\n *\n * Determines which toll roads the routing engine may use: some tolls can only be paid by\n * transponder, others only without one.\n *\n * @group Vehicle\n */\nexport const tollTransponderOptions = ['all', 'unknown', 'none'] as const;\n\n/**\n * Whether the vehicle carries an electronic toll collection transponder.\n *\n * @remarks\n * - `all` — the vehicle has a transponder accepted by every toll operator on the route\n * - `unknown` — transponder availability is unknown, so no assumption is made\n * - `none` — the vehicle has no transponder\n *\n * @example\n * ```typescript\n * const transponder: TollTransponder = 'all';\n * ```\n *\n * @group Vehicle\n */\nexport type TollTransponder = (typeof tollTransponderOptions)[number];\n\n/**\n * Parameters for a vehicle which are related to restrictions applied during route planning.\n */\nexport type VehicleRestrictions = {\n /**\n * Vehicle restrictions applied during route planning.\n */\n restrictions?: {\n /**\n * Maximum speed of the vehicle in kilometers/hour.\n * * Must have a value in the range [0, 250].\n * * A value of 0 means that an appropriate value for the vehicle will be determined and applied during route planning.\n *\n * @default 0\n */\n maxSpeedKMH?: number;\n\n /**\n * Whether the vehicle carries an electronic toll collection transponder.\n *\n * @remarks\n * Affects which toll roads are considered usable: a toll that can only be paid by\n * transponder is avoided when this is `none`, and a toll that cannot be paid by\n * transponder is avoided when this is `all`.\n *\n * @default 'unknown'\n *\n * @example\n * ```typescript\n * restrictions: {\n * tollTransponder: 'all'\n * }\n * ```\n */\n tollTransponder?: TollTransponder;\n };\n};\n","import type { ParseResponseError } from '../shared';\nimport { SDKServiceError } from '../shared';\n\n/**\n * @ignore\n * @param apiError\n * @param serviceName\n */\nexport const parseEVChargingStationsAvailabilityResponseError: ParseResponseError = (apiError, serviceName) => {\n const errorMessage = apiError.data?.detailedError?.message ?? apiError.message;\n return new SDKServiceError(errorMessage, serviceName, apiError.status);\n};\n","import { PLACES_URL_PATH } from '../shared/request/commonSearchRequestBuilder';\nimport { appendCommonParams } from '../shared/request/requestBuildingUtils';\nimport type { ChargingStationsAvailabilityParams } from './types/evChargingStationsAvailabilityParams';\n\nconst buildUrlBasePath = (params: ChargingStationsAvailabilityParams): string =>\n params.customServiceBaseURL ?? `${params.commonBaseURL}${PLACES_URL_PATH}/ev/id`;\n\n/**\n * Default method for building ev charging stations availability request from {@link ChargingStationsAvailabilityParams}\n * @param params The charging availability parameters, with global configuration already merged into them.\n */\nexport const buildEVChargingStationsAvailabilityRequest = (params: ChargingStationsAvailabilityParams): URL => {\n const url = new URL(buildUrlBasePath(params));\n const urlParams = url.searchParams;\n appendCommonParams(urlParams, params);\n urlParams.append('id', params.id);\n return url;\n};\n","import type { ChargingStation, ChargingStationsAvailability } from '@tomtom-org/maps-sdk/core';\nimport { toChargingSpeed } from '../shared/ev';\nimport { parseOpeningHours } from '../shared/searchResultParsing';\nimport { toChargingPointAvailability, toConnectorBasedAvailabilities } from './connectorAvailability';\nimport type { ChargingStationsAvailabilityResponseAPI } from './types/apiTypes';\n\nconst toChargingPointStations = (stations: ChargingStation[]) =>\n stations.map((station) => ({\n ...station,\n chargingPoints: station.chargingPoints.map((chargingPoint) => ({\n ...chargingPoint,\n connectors: chargingPoint.connectors?.map((connector) => ({\n ...connector,\n chargingSpeed: toChargingSpeed(connector.ratedPowerKW),\n })),\n })),\n }));\n\n/**\n * Default method for parsing ev charging stations availability from {@link ChargingStationsAvailability}\n * @param apiResponse\n */\nexport const parseEVChargingStationsAvailabilityResponse = (\n apiResponse: ChargingStationsAvailabilityResponseAPI,\n): ChargingStationsAvailability | undefined => {\n const result = apiResponse.results?.[0];\n return result\n ? {\n id: result.id,\n accessType: result.accessType,\n chargingStations: toChargingPointStations(result.chargingStations),\n chargingPointAvailability: toChargingPointAvailability(result.chargingStations),\n connectorAvailabilities: toConnectorBasedAvailabilities(result.chargingStations),\n ...(result.openingHours && { openingHours: parseOpeningHours(result.openingHours) }),\n }\n : undefined;\n};\n","import type { ChargingStationsAvailability } from '@tomtom-org/maps-sdk/core';\nimport type { ServiceTemplate } from '../shared';\nimport { get } from '../shared/fetch';\nimport { evChargingStationsAvailabilityRequestSchema } from './evChargingStationsAvailabilityRequestSchema';\nimport { parseEVChargingStationsAvailabilityResponseError } from './evChargingStationsAvailabilityResponseErrorParser';\nimport { buildEVChargingStationsAvailabilityRequest } from './requestBuilder';\nimport { parseEVChargingStationsAvailabilityResponse } from './responseParser';\nimport type { ChargingStationsAvailabilityResponseAPI } from './types/apiTypes';\nimport type { ChargingStationsAvailabilityParams } from './types/evChargingStationsAvailabilityParams';\n\n/**\n * EV Charging Stations Availability service template type.\n * @ignore\n */\nexport type EVChargingStationsAvailabilityTemplate = ServiceTemplate<\n ChargingStationsAvailabilityParams,\n URL,\n ChargingStationsAvailabilityResponseAPI,\n ChargingStationsAvailability | undefined\n>;\n\n/**\n * EV Charging Stations Availability service template main implementation.\n * @ignore\n */\nexport const evChargingStationsAvailabilityTemplate: EVChargingStationsAvailabilityTemplate = {\n requestValidation: { schema: evChargingStationsAvailabilityRequestSchema },\n buildRequest: buildEVChargingStationsAvailabilityRequest,\n sendRequest: get,\n parseResponse: parseEVChargingStationsAvailabilityResponse,\n parseResponseError: parseEVChargingStationsAvailabilityResponseError,\n};\n","import type { EVChargingStationsAvailabilityTemplate } from './evChargingStationsAvailabilityTemplate';\nimport { evChargingStationsAvailabilityTemplate } from './evChargingStationsAvailabilityTemplate';\nimport { buildEVChargingStationsAvailabilityRequest } from './requestBuilder';\nimport { parseEVChargingStationsAvailabilityResponse } from './responseParser';\n\nconst customize: {\n buildEVChargingStationsAvailabilityRequest: typeof buildEVChargingStationsAvailabilityRequest;\n parseEVChargingStationsAvailabilityResponse: typeof parseEVChargingStationsAvailabilityResponse;\n evChargingStationsAvailabilityTemplate: EVChargingStationsAvailabilityTemplate;\n} = {\n buildEVChargingStationsAvailabilityRequest,\n parseEVChargingStationsAvailabilityResponse,\n evChargingStationsAvailabilityTemplate,\n};\nexport default customize;\n","import { z } from 'zod';\nimport { hasBBoxSchema } from './geometriesSchema';\n\n/**\n * @ignore\n */\nexport const commonGeocodeAndFuzzySearchParamsSchema = z.object({\n typeahead: z.boolean().optional(),\n offset: z.number().max(1900).optional(),\n radiusMeters: z.number().optional(),\n boundingBox: hasBBoxSchema.optional(),\n countries: z.array(z.string()).optional(),\n});\n","import { z } from 'zod';\nimport { commonGeocodeAndFuzzySearchParamsSchema } from '../shared/schema/commonGeocodeAndFuzzySearchParamsSchema';\nimport { commonServiceRequestSchema } from '../shared/schema/commonParamsSchema';\nimport { commonPlacesParamsSchema } from '../shared/schema/commonPlacesParamsSchema';\n\n/**\n * @ignore\n */\nexport const geocodingRequestSchema = commonServiceRequestSchema\n .extend(commonPlacesParamsSchema.shape)\n .extend(commonGeocodeAndFuzzySearchParamsSchema.shape)\n .extend({ query: z.string() });\n","/**\n * @ignore\n * @param input\n */\nexport const arrayToCSV = (input: unknown | unknown[]): string =>\n !input ? '' : Array.isArray(input) ? input.join(',') : typeof input === 'string' ? input : String(input);\n\n/**\n * Samples, if necessary, the given array to fit within the given max length.\n * * If the array already fits within maxLength, the same array is returned.\n * * The sampling is done by spreading the array points at a constant increment.\n * * The first and last points are always included if array and max lengths are > 1.\n * * The sampling is done with speed in mind, so it's not guaranteed to always fit maxLength. Very often it will be shorter.\n *\n * Use this when needing to quickly simplify an array to fit some max length without caring too much on the lost detail.\n * * If both the array and max length are long enough, the loss of detail won't likely be an issue.\n * @ignore\n */\nexport const sampleWithinMaxLength = <T>(array: T[], maxLength: number): T[] => {\n const length = array.length;\n if (length <= maxLength) {\n return array;\n }\n\n const sampledArray = [];\n let i;\n const increment = Math.ceil(length / maxLength);\n for (i = 0; i < length; i += increment) {\n sampledArray.push(array[i]);\n }\n // ensuring the last point is always added:\n if (maxLength > 1 && i >= length - increment) {\n if (sampledArray.length < maxLength) {\n sampledArray.push(array[length - 1]);\n } else {\n sampledArray[sampledArray.length - 1] = array[length - 1];\n }\n }\n\n return sampledArray;\n};\n","import { bboxFromGeoJSON } from '@tomtom-org/maps-sdk/core';\nimport { isNil } from 'lodash-es';\nimport { arrayToCSV } from '../shared/arrays';\nimport { PLACES_URL_PATH } from '../shared/request/commonSearchRequestBuilder';\nimport { appendCommonParams, appendLatLonParamsFromPosition } from '../shared/request/requestBuildingUtils';\nimport type { GeocodingParams } from './types/geocodingParams';\n\nconst buildUrlBasePath = (params: GeocodingParams): string =>\n params.customServiceBaseURL || `${params.commonBaseURL}${PLACES_URL_PATH}/geocode`;\n\n/**\n * Default method for building geocoding request from {@link GeocodingParams}\n * @param params The geocoding parameters, with global configuration already merged into them.\n */\nexport const buildGeocodingRequest = (params: GeocodingParams): URL => {\n const url = new URL(`${buildUrlBasePath(params)}/${params.query}.json`);\n const urlParams = url.searchParams;\n appendCommonParams(urlParams, params);\n // geocoding specific parameters:\n params.typeahead && urlParams.append('typeahead', String(params.typeahead));\n !isNil(params.limit) && urlParams.append('limit', String(params.limit));\n !isNil(params.offset) && urlParams.append('ofs', String(params.offset));\n appendLatLonParamsFromPosition(urlParams, params.position);\n params.countries && urlParams.append('countrySet', arrayToCSV(params.countries));\n !isNil(params.radiusMeters) && urlParams.append('radius', String(params.radiusMeters));\n const bbox = params.boundingBox && bboxFromGeoJSON(params.boundingBox);\n if (bbox) {\n urlParams.append('topLeft', arrayToCSV([bbox[3], bbox[0]]));\n urlParams.append('btmRight', arrayToCSV([bbox[1], bbox[2]]));\n }\n params.extendedPostalCodesFor &&\n urlParams.append('extendedPostalCodesFor', arrayToCSV(params.extendedPostalCodesFor));\n params.mapcodes && urlParams.append('mapcodes', arrayToCSV(params.mapcodes));\n params.view && urlParams.append('view', params.view);\n params.geographyTypes && urlParams.append('entityTypeSet', arrayToCSV(params.geographyTypes));\n return url;\n};\n","import type { GeographyType, Place } from '@tomtom-org/maps-sdk/core';\nimport { bboxFromGeoJSON, bboxOnlyIfWithArea, toPointGeometry } from '@tomtom-org/maps-sdk/core';\nimport { omit } from 'lodash-es';\nimport { apiToGeoJSONBBox, latLonAPIToPosition } from '../shared/geometry';\nimport type { GeocodingResponseAPI, GeocodingResultAPI } from './types/apiTypes';\nimport type { GeocodingProps, GeocodingResponse } from './types/geocodingResponse';\n\nconst parseApiResult = (result: GeocodingResultAPI): Place<GeocodingProps> => {\n const { position, boundingBox, dist, entryPoints, addressRanges, entityType, id, ...rest } = result;\n\n return {\n type: 'Feature',\n geometry: toPointGeometry(latLonAPIToPosition(position)),\n ...(boundingBox && { bbox: apiToGeoJSONBBox(boundingBox) }),\n id,\n properties: {\n ...omit(rest, 'viewport'),\n ...(dist && { distance: dist }),\n ...(entityType && { geographyType: entityType.split(',') as GeographyType[] }),\n ...(entryPoints && {\n entryPoints: entryPoints.map((entrypoint) => ({\n ...entrypoint,\n position: latLonAPIToPosition(entrypoint.position),\n })),\n }),\n ...(addressRanges && {\n addressRanges: {\n ...addressRanges,\n from: latLonAPIToPosition(addressRanges.from),\n to: latLonAPIToPosition(addressRanges.to),\n },\n }),\n },\n };\n};\n\n/**\n * Default method for parsing geocoding request from {@link GeocodingResponse}\n * @param apiResponse\n */\nexport const parseGeocodingResponse = (apiResponse: GeocodingResponseAPI): GeocodingResponse => {\n const results = apiResponse.results;\n const features = results.map(parseApiResult);\n const bbox = bboxOnlyIfWithArea(bboxFromGeoJSON(features));\n return {\n type: 'FeatureCollection',\n features,\n ...(bbox && { bbox }),\n };\n};\n","import type { ServiceTemplate } from '../shared';\nimport { get } from '../shared/fetch';\nimport { geocodingRequestSchema } from './geocodingRequestSchema';\nimport { buildGeocodingRequest } from './requestBuilder';\nimport { parseGeocodingResponse } from './responseParser';\nimport type { GeocodingResponseAPI } from './types/apiTypes';\nimport type { GeocodingParams } from './types/geocodingParams';\nimport type { GeocodingResponse } from './types/geocodingResponse';\n\n/**\n * Geocoding service template type.\n * @ignore\n */\nexport type GeocodingTemplate = ServiceTemplate<GeocodingParams, URL, GeocodingResponseAPI, GeocodingResponse>;\n\n/**\n * Geocoding service template main implementation.\n * @ignore\n */\nexport const geocodingTemplate: GeocodingTemplate = {\n requestValidation: { schema: geocodingRequestSchema },\n buildRequest: buildGeocodingRequest,\n sendRequest: get,\n parseResponse: parseGeocodingResponse,\n};\n","import type { GeocodingTemplate } from './geocodingTemplate';\nimport { geocodingTemplate } from './geocodingTemplate';\nimport { buildGeocodingRequest } from './requestBuilder';\nimport { parseGeocodingResponse } from './responseParser';\n\nconst customize: {\n buildGeocodingRequest: typeof buildGeocodingRequest;\n parseGeocodingResponse: typeof parseGeocodingResponse;\n geocodingTemplate: GeocodingTemplate;\n} = {\n buildGeocodingRequest,\n parseGeocodingResponse,\n geocodingTemplate,\n};\nexport default customize;\n","import { z } from 'zod';\nimport { featureCollectionSchema, featureSchema } from '../shared/schema/geometriesSchema';\n\nconst geometryDataRequestMandatory = z.object({\n geometries: z.union([\n featureCollectionSchema,\n z\n .array(z.union([z.string(), featureSchema]))\n .min(1)\n .max(20),\n ]),\n});\n\nconst geometryDataRequestOptional = z.object({\n zoom: z.number().min(0).max(22).optional(),\n});\n\nexport const geometryDataRequestSchema = geometryDataRequestMandatory.extend(geometryDataRequestOptional.shape);\n","import type { Place, Places } from '@tomtom-org/maps-sdk/core';\nimport { arrayToCSV } from '../shared/arrays';\nimport { PLACES_URL_PATH } from '../shared/request/commonSearchRequestBuilder';\nimport { appendOptionalParam } from '../shared/request/requestBuildingUtils';\nimport type { GeometriesInput, GeometryParams } from './types/geometryDataParams';\n\nconst buildUrlBasePath = (params: GeometryParams): string =>\n params.customServiceBaseURL || `${params.commonBaseURL}${PLACES_URL_PATH}/additionalData.json`;\n\nconst getGeometryIDs = (placesArray: Place[]): string[] =>\n placesArray.map((place) => place.properties.dataSources?.geometry?.id as string).filter((id) => id);\n\n// (@see geometryDataRequestSchema)\nconst appendGeometries = (urlParams: URLSearchParams, geometries: GeometriesInput | Places | Place[]): void => {\n let geometryIDs: string[];\n\n if (Array.isArray(geometries)) {\n // (assuming min and max length already validated)\n if (typeof geometries[0] === 'string') {\n geometryIDs = geometries as string[];\n } else {\n geometryIDs = getGeometryIDs(geometries as Place[]);\n }\n } else {\n // (assuming already validated as FeatureCollection)\n geometryIDs = getGeometryIDs(geometries.features);\n }\n\n urlParams.append('geometries', arrayToCSV(geometryIDs));\n};\n\n/**\n * Default function for building a geometry data request from {@link GeometryDataParams}\n * @param params The geometry data parameters, with global configuration already merged into them.\n */\nexport const buildGeometryDataRequest = (params: GeometryParams): URL => {\n const url = new URL(buildUrlBasePath(params));\n const urlParams = url.searchParams;\n // (no language in this service)\n urlParams.append('apiVersion', String(params.apiVersion));\n if (params.apiKey) {\n urlParams.append('key', params.apiKey);\n }\n appendGeometries(urlParams, params.geometries);\n appendOptionalParam(urlParams, 'geometriesZoom', params.zoom);\n return url;\n};\n","import { bboxFromGeoJSON, generateId, PolygonFeature, PolygonFeatures } from '@tomtom-org/maps-sdk/core';\nimport type { GeometryDataResponseAPI } from './types/apiTypes';\n\n/**\n * Default geometry data API response parsing.\n * * The API response consists of an array, with a FeatureCollection (with only one feature) for each geometry.\n * * The parsed response consists of a fully-GeoJSON-compatible FeatureCollection with a Feature for each geometry.\n * * Each geometry ID is included in each GeoJSON feature \"id\" field.\n * @param apiResponse\n */\nexport const parseGeometryDataResponse = (apiResponse: GeometryDataResponseAPI): PolygonFeatures => {\n const features = apiResponse.additionalData\n .flatMap((data) =>\n (data.geometryData as PolygonFeatures)?.features.map((feature) => ({\n ...feature,\n id: feature.id ?? generateId(),\n bbox: bboxFromGeoJSON(feature.geometry),\n })),\n )\n .filter((feature) => feature) as PolygonFeature[];\n return {\n type: 'FeatureCollection',\n bbox: bboxFromGeoJSON(features),\n features,\n };\n};\n","import type { PolygonFeatures } from '@tomtom-org/maps-sdk/core';\nimport type { ServiceTemplate } from '../shared';\nimport { get } from '../shared/fetch';\nimport { geometryDataRequestSchema } from './geometryDataRequestSchema';\nimport { buildGeometryDataRequest } from './requestBuilder';\nimport { parseGeometryDataResponse } from './responseParser';\nimport type { GeometryDataResponseAPI } from './types/apiTypes';\nimport type { GeometryParams } from './types/geometryDataParams';\n\n/**\n * @ignore\n */\nexport type GeometryDataTemplate = ServiceTemplate<GeometryParams, URL, GeometryDataResponseAPI, PolygonFeatures>;\n\n/**\n * @ignore\n */\nexport const geometryDataTemplate: GeometryDataTemplate = {\n requestValidation: { schema: geometryDataRequestSchema },\n buildRequest: buildGeometryDataRequest,\n sendRequest: get,\n parseResponse: parseGeometryDataResponse,\n};\n","import type { GeometryDataTemplate } from './geometryDataTemplate';\nimport { geometryDataTemplate } from './geometryDataTemplate';\nimport { buildGeometryDataRequest } from './requestBuilder';\nimport { parseGeometryDataResponse } from './responseParser';\n\nconst customize: {\n buildGeometryDataRequest: typeof buildGeometryDataRequest;\n parseGeometryDataResponse: typeof parseGeometryDataResponse;\n geometryDataTemplate: GeometryDataTemplate;\n} = {\n buildGeometryDataRequest,\n parseGeometryDataResponse,\n geometryDataTemplate,\n};\nexport default customize;\n","import { z } from 'zod';\nimport { commonSearchParamsSchema } from '../search/commonSearchParamsSchema';\nimport { featureCollectionSchema, geometrySchema } from '../shared/schema/geometriesSchema';\n\nconst geometrySearchRequestMandatory = z.object({\n geometries: z.array(z.union([featureCollectionSchema, geometrySchema])),\n});\n\n/**\n * @ignore\n */\nexport const geometrySearchRequestSchema = commonSearchParamsSchema.extend(geometrySearchRequestMandatory.shape);\n","import { bboxFromCoordsArray } from '@tomtom-org/maps-sdk/core';\nimport type { MultiPolygon, Position } from 'geojson';\nimport type { PostObject } from '../shared';\nimport { sampleWithinMaxLength } from '../shared/arrays';\nimport { positionToCSVLatLon } from '../shared/geometry';\nimport { appendCommonSearchParams, PLACES_URL_PATH } from '../shared/request/commonSearchRequestBuilder';\nimport type { GeometryAPI, GeometrySearchParams, GeometrySearchPayloadAPI, SearchGeometryInput } from './types';\n\nconst findFiftyLargestPolygons = (searchGeometry: MultiPolygon): Position[][][] => {\n // we calculate the size of each polygon based on bounding box (simplified)\n // we put it into a map for easy sorting\n // we sort the map by using size and keep only 50 largest polygons\n let polygonSizeMap = new Map<Position[][], number>();\n searchGeometry.coordinates.forEach((polygon) => {\n const bboxOfPolygon = bboxFromCoordsArray(polygon[0]);\n if (bboxOfPolygon) {\n const polygonSize = Math.abs((bboxOfPolygon[2] - bboxOfPolygon[0]) * (bboxOfPolygon[3] - bboxOfPolygon[1]));\n polygonSizeMap.set(polygon, polygonSize);\n }\n });\n polygonSizeMap = new Map([...polygonSizeMap.entries()].sort((a, b) => b[1] - a[1]).splice(0, 50));\n return [...polygonSizeMap.keys()];\n};\n\nconst sdkGeometryToApiGeometries = (searchGeometry: SearchGeometryInput): GeometryAPI[] => {\n switch (searchGeometry.type) {\n case 'Circle':\n return [\n {\n type: 'CIRCLE',\n radius: searchGeometry.radius,\n position: positionToCSVLatLon(searchGeometry.coordinates),\n },\n ];\n case 'Polygon':\n return [\n {\n type: 'POLYGON',\n vertices: sampleWithinMaxLength(searchGeometry.coordinates[0], 50).map((coord) =>\n positionToCSVLatLon(coord),\n ),\n },\n ];\n case 'MultiPolygon': {\n if (searchGeometry.coordinates.length > 50) {\n // we have too many polygons for the service to work\n return findFiftyLargestPolygons(searchGeometry).flatMap((polygonCoords) =>\n sdkGeometryToApiGeometries({ type: 'Polygon', coordinates: polygonCoords }),\n );\n }\n return searchGeometry.coordinates.flatMap((polygonCoords) =>\n sdkGeometryToApiGeometries({ type: 'Polygon', coordinates: polygonCoords }),\n );\n }\n case 'FeatureCollection':\n return searchGeometry.features.flatMap((feature) => sdkGeometryToApiGeometries(feature.geometry));\n default:\n // @ts-ignore\n throw new Error(`Type ${(searchGeometry as unknown).type} is not supported`);\n }\n};\n\nconst buildUrlBasePath = (mergedOptions: GeometrySearchParams): string =>\n mergedOptions.customServiceBaseURL ??\n `${mergedOptions.commonBaseURL}${PLACES_URL_PATH}/geometrySearch/${mergedOptions.query ?? ''}.json`;\n\n/**\n * Default function for building a geometry search request from {@link GeometrySearchParams}\n * @param params The geometry search parameters, with global configuration already merged into them.\n */\nexport const buildGeometrySearchRequest = (params: GeometrySearchParams): PostObject<GeometrySearchPayloadAPI> => {\n const url = new URL(`${buildUrlBasePath(params)}`);\n appendCommonSearchParams(url, params);\n\n return {\n url,\n data: {\n geometryList: params.geometries.flatMap(sdkGeometryToApiGeometries),\n },\n };\n};\n","import { bboxFromGeoJSON, bboxOnlyIfWithArea } from '@tomtom-org/maps-sdk/core';\nimport { parseSearchAPIResult, parseSummaryAPI } from '../shared/searchResultParsing';\nimport type { GeometrySearchResponse, GeometrySearchResponseAPI } from './types';\n\n/**\n * Default function to parse a geometry search response.\n * @param apiResponse The API response.\n */\nexport const parseGeometrySearchResponse = (apiResponse: GeometrySearchResponseAPI): GeometrySearchResponse => {\n const features = apiResponse.results.map(parseSearchAPIResult);\n const bbox = bboxOnlyIfWithArea(bboxFromGeoJSON(features));\n return {\n type: 'FeatureCollection',\n properties: {\n ...parseSummaryAPI(apiResponse.summary),\n },\n features,\n ...(bbox && { bbox }),\n };\n};\n","import type { ServiceTemplate } from '../shared';\nimport { post } from '../shared/fetch';\nimport { geometrySearchRequestSchema } from './geometrySearchRequestSchema';\nimport { buildGeometrySearchRequest } from './requestBuilder';\nimport { parseGeometrySearchResponse } from './responseParser';\nimport type {\n GeometrySearchParams,\n GeometrySearchRequestAPI,\n GeometrySearchResponse,\n GeometrySearchResponseAPI,\n} from './types';\n\n/**\n * Geometry search service template type.\n */\nexport type GeometrySearchTemplate = ServiceTemplate<\n GeometrySearchParams,\n GeometrySearchRequestAPI,\n GeometrySearchResponseAPI,\n GeometrySearchResponse\n>;\n\n/**\n * Geometry search service template main implementation.\n */\nexport const geometrySearchTemplate: GeometrySearchTemplate = {\n requestValidation: { schema: geometrySearchRequestSchema },\n buildRequest: buildGeometrySearchRequest,\n sendRequest: post,\n parseResponse: parseGeometrySearchResponse,\n};\n","import { callService } from '../shared/serviceTemplate';\nimport type { GeometrySearchTemplate } from './geometrySearchTemplate';\nimport { geometrySearchTemplate } from './geometrySearchTemplate';\nimport type { GeometrySearchParams, GeometrySearchResponse } from './types';\n\n/**\n * Search for places within specific geographic boundaries.\n *\n * The Geometry Search service finds places that fall within one or more defined areas,\n * such as polygons, circles, or corridors. This is essential for location-based filtering\n * and spatial queries where you need results constrained to specific regions.\n *\n * @remarks\n * Key features:\n * - **Area-based search**: Find places within polygons, circles, or along routes\n * - **Multiple geometries**: Search across several areas simultaneously\n * - **Precise boundaries**: Only returns results within the specified areas\n * - **Combined with text**: Filter by query text within the geometric boundaries\n * - **Category filtering**: Narrow results by POI categories\n *\n * Common use cases:\n * - **Route-based search**: \"Find gas stations along my route\"\n * - **Area filtering**: \"Restaurants within this neighborhood polygon\"\n * - **Corridor search**: \"Hotels within 5km of the highway\"\n * - **Service areas**: \"Stores within our delivery zone\"\n * - **Geofencing**: Places within administrative or custom boundaries\n *\n * @param params Geometry search parameters including geometries and optional query\n * @param customTemplate Advanced customization for request/response handling\n *\n * @returns Promise resolving to places within the specified geometries\n *\n * @example\n * ```typescript\n * // Search within a polygon (neighborhood boundaries)\n * const inArea = await geometrySearch({\n * query: 'coffee shop',\n * geometries: [{\n * type: 'Polygon',\n * coordinates: [[\n * [4.88, 52.36],\n * [4.90, 52.36],\n * [4.90, 52.38],\n * [4.88, 52.38],\n * [4.88, 52.36]\n * ]]\n * }]\n * });\n *\n * // Find POIs along a route (pass the route LineString as geometry)\n * const alongRoute = await geometrySearch({\n * query: 'gas station',\n * geometries: [routeLineString], // LineString from calculateRoute result\n * limit: 10\n * });\n *\n * // Search multiple areas at once\n * const multiArea = await geometrySearch({\n * query: 'pharmacy',\n * geometries: [polygonA, polygonB, polygonC],\n * limit: 20\n * });\n *\n * // Category search within geometry\n * const restaurants = await geometrySearch({\n * poiCategories: ['ITALIAN_RESTAURANT'],\n * geometries: [cityBoundary]\n * });\n * ```\n *\n * @see [Geometry Search API Documentation](https://docs.tomtom.com/search-api/documentation/search-service/geometry-search)\n * @see [Places Quickstart Guide](https://docs.tomtom.com/maps-sdk-js/guides/services/places/quickstart)\n * @see [Search Guide](https://docs.tomtom.com/maps-sdk-js/guides/services/places/search)\n *\n * @ignore (exposed via 'search')\n */\nexport const geometrySearch = async (\n params: GeometrySearchParams,\n customTemplate?: Partial<GeometrySearchTemplate>,\n): Promise<GeometrySearchResponse> =>\n callService(params, { ...geometrySearchTemplate, ...customTemplate }, 'GeometrySearch');\n","import { geometrySearch } from './geometrySearch';\nimport type { GeometrySearchTemplate } from './geometrySearchTemplate';\nimport { geometrySearchTemplate } from './geometrySearchTemplate';\nimport { buildGeometrySearchRequest } from './requestBuilder';\nimport { parseGeometrySearchResponse } from './responseParser';\n\nconst customize: {\n geometrySearch: typeof geometrySearch;\n buildGeometrySearchRequest: typeof buildGeometrySearchRequest;\n parseGeometrySearchResponse: typeof parseGeometrySearchResponse;\n geometrySearchTemplate: GeometrySearchTemplate;\n} = {\n geometrySearch,\n buildGeometrySearchRequest,\n parseGeometrySearchResponse,\n geometrySearchTemplate,\n};\nexport default customize;\n","import { views } from '@tomtom-org/maps-sdk/core';\nimport { z } from 'zod';\n\nconst placeByIdRequestMandatory = z.object({\n entityId: z.string(),\n});\n\nconst placeByIdRequestOptional = z.object({\n mapcodes: z.array(z.string()).optional(),\n view: z.enum(views).optional(),\n openingHours: z.string().optional(),\n timeZone: z.string().optional(),\n relatedPois: z.string().optional(),\n});\n\n/**\n * @ignore\n */\nexport const placeByIdRequestSchema = placeByIdRequestMandatory.extend(placeByIdRequestOptional.shape);\n","import type { CommonServiceParams } from '../shared';\nimport { PLACES_URL_PATH } from '../shared/request/commonSearchRequestBuilder';\nimport {\n appendByJoiningParamValue,\n appendCommonParams,\n appendOptionalParam,\n} from '../shared/request/requestBuildingUtils';\nimport type { PlaceByIdParams } from './types';\n\nconst buildUrlBasePath = (params: CommonServiceParams): string =>\n params.customServiceBaseURL || `${params.commonBaseURL}${PLACES_URL_PATH}/place.json`;\n\n/**\n * Default method for building place by id request from {@link PlaceByIdParams}\n * @param params The place by id parameters, with global configuration already merged into them.\n */\nexport const buildPlaceByIdRequest = (params: PlaceByIdParams): URL => {\n const url = new URL(`${buildUrlBasePath(params)}`);\n const urlParams = url.searchParams;\n appendCommonParams(urlParams, params);\n appendOptionalParam(urlParams, 'entityId', params.entityId);\n appendByJoiningParamValue(urlParams, 'mapcodes', params.mapcodes);\n appendOptionalParam(urlParams, 'view', params.view);\n appendOptionalParam(urlParams, 'openingHours', params.openingHours);\n appendOptionalParam(urlParams, 'timeZone', params.timeZone);\n appendOptionalParam(urlParams, 'relatedPois', params.relatedPois);\n return url;\n};\n","import { parseSearchAPIResult } from '../shared/searchResultParsing';\nimport type { PlaceByIdResponse, PlaceByIdResponseAPI } from './types';\n\n/**\n * Default method for parsing place by id response.\n * @param apiResponse The place by id response.\n */\nexport const parsePlaceByIdResponse = (apiResponse: PlaceByIdResponseAPI): PlaceByIdResponse =>\n apiResponse.results?.length ? parseSearchAPIResult(apiResponse.results[0]) : undefined;\n","import type { ServiceTemplate } from '../shared';\nimport { get } from '../shared/fetch';\nimport { placeByIdRequestSchema } from './placeByIdSchema';\nimport { buildPlaceByIdRequest } from './requestBuilder';\nimport { parsePlaceByIdResponse } from './responseParser';\nimport type { PlaceByIdParams, PlaceByIdResponse, PlaceByIdResponseAPI } from './types';\n\n/**\n * Place By Is template type.\n * @ignore\n */\nexport type PlaceByIdTemplate = ServiceTemplate<PlaceByIdParams, URL, PlaceByIdResponseAPI, PlaceByIdResponse>;\n\n/**\n * Place By Id template main implementation.\n * @ignore\n */\nexport const placeByIdTemplate: PlaceByIdTemplate = {\n requestValidation: { schema: placeByIdRequestSchema },\n buildRequest: buildPlaceByIdRequest,\n sendRequest: get,\n parseResponse: parsePlaceByIdResponse,\n};\n","import type { PlaceByIdTemplate } from './placeByIdTemplate';\nimport { placeByIdTemplate } from './placeByIdTemplate';\nimport { buildPlaceByIdRequest } from './requestBuilder';\nimport { parsePlaceByIdResponse } from './responseParser';\n\nconst customize: {\n buildPlaceByIdRequest: typeof buildPlaceByIdRequest;\n parsePlaceByIdResponse: typeof parsePlaceByIdResponse;\n placeByIdTemplate: PlaceByIdTemplate;\n} = {\n buildPlaceByIdRequest,\n parsePlaceByIdResponse,\n placeByIdTemplate,\n};\nexport default customize;\n","import type { ParseResponseError } from '../shared';\nimport { SDKServiceError } from '../shared';\nimport type { RoutingAPIResponseError } from '../shared/types/apiResponseErrorTypes';\n\n/**\n * @ignore\n * @param apiError\n * @param serviceName\n */\nexport const parseRoutingResponseError: ParseResponseError<RoutingAPIResponseError> = (apiError, serviceName) => {\n const { data, message, status } = apiError;\n const errorMessage = data?.error?.description ?? data?.detailedError?.message ?? message;\n return new SDKServiceError(errorMessage, serviceName, status);\n};\n","import { currentTypes, plugTypes } from '@tomtom-org/maps-sdk/core';\nimport { z } from 'zod';\nimport { tollTransponderOptions } from '../types/vehicleRestrictionParams';\n\n// Common validation schemas\nconst positiveNumber = z.number().positive();\nconst nonNegativeNumber = z.number().min(0);\nconst percentageNumber = z.number().min(0).max(100);\n\n// Common optional schemas\nconst optionalPositiveNumber = positiveNumber.optional();\nconst optionalNonNegativeNumber = nonNegativeNumber.optional();\nconst optionalNormalizedNumber = z.number().min(0).max(1).optional();\n\n// Speed to consumption rate schema\nconst speedToConsumptionRateSchema = z.object({\n speedKMH: z.number(),\n consumptionUnitsPer100KM: z.number(),\n});\n\n// Efficiency schema for consumption models\nconst efficiencySchema = z\n .object({\n acceleration: optionalNormalizedNumber,\n deceleration: optionalNormalizedNumber,\n uphill: optionalNormalizedNumber,\n downhill: optionalNormalizedNumber,\n })\n .optional();\n\n// Base consumption model schema\nconst baseConsumptionModelSchema = {\n efficiency: efficiencySchema,\n};\n\n// Consumption array validation\nconst speedConsumptionArray = z.array(speedToConsumptionRateSchema).min(1).max(25);\n\n// Combustion consumption model schema\nconst combustionConsumptionModelSchema = z.object({\n ...baseConsumptionModelSchema,\n speedsToConsumptionsLiters: speedConsumptionArray,\n auxiliaryPowerInLitersPerHour: optionalNonNegativeNumber,\n fuelEnergyDensityInMJoulesPerLiter: z.number().min(1).optional(),\n});\n\n// Electric consumption model schema\nconst electricConsumptionModelSchema = z.object({\n ...baseConsumptionModelSchema,\n speedsToConsumptionsKWH: speedConsumptionArray,\n auxiliaryPowerInkW: optionalNonNegativeNumber,\n consumptionInKWHPerKMAltitudeGain: z.number().max(500).optional(),\n recuperationInKWHPerKMAltitudeLoss: optionalNonNegativeNumber,\n});\n\n// Charging connector schema\nconst chargingConnectorSchema = z.object({\n currentType: z.enum(currentTypes),\n plugTypes: z.array(z.enum(plugTypes)).min(1),\n efficiency: optionalNormalizedNumber,\n baseLoadInkW: optionalNonNegativeNumber,\n maxPowerInkW: optionalNonNegativeNumber,\n maxVoltageInV: optionalNonNegativeNumber,\n maxCurrentInA: optionalNonNegativeNumber,\n voltageRange: z\n .object({\n minVoltageInV: optionalNonNegativeNumber,\n maxVoltageInV: z.number().optional(),\n })\n .optional(),\n});\n\n// Battery curve schema\nconst batteryCurveSchema = z.object({\n stateOfChargeInkWh: nonNegativeNumber,\n maxPowerInkW: positiveNumber,\n});\n\n// Charging model schema\nconst chargingModelSchema = z.object({\n maxChargeKWH: positiveNumber,\n batteryCurve: z.array(batteryCurveSchema).max(20).optional(),\n chargingConnectors: z.array(chargingConnectorSchema).min(1).optional(),\n chargingTimeOffsetInSec: optionalNonNegativeNumber,\n});\n\n// Engine model schemas\nconst combustionEngineModelSchema = z.object({\n consumption: combustionConsumptionModelSchema,\n});\nconst electricEngineModelSchema = z.object({\n consumption: electricConsumptionModelSchema,\n charging: chargingModelSchema.optional(),\n});\n\n// Vehicle dimensions schema\nconst vehicleDimensionsSchema = z\n .object({\n weightKG: optionalPositiveNumber,\n })\n .optional();\n\n// Vehicle model schemas\nconst predefinedVehicleModelSchema = z.object({\n variantId: z.string(),\n});\nconst explicitVehicleModelSchema = z.object({\n dimensions: vehicleDimensionsSchema,\n engine: z.union([combustionEngineModelSchema, electricEngineModelSchema]).optional(),\n});\nconst vehicleModelSchema = z.union([predefinedVehicleModelSchema, explicitVehicleModelSchema]).optional();\n\n// Vehicle state schemas\nconst genericVehicleStateSchema = z.object({\n heading: z.number().min(0).max(360).optional(),\n});\n\nconst combustionVehicleStateSchema = genericVehicleStateSchema.extend({\n currentFuelInLiters: nonNegativeNumber,\n});\n\nconst electricVehicleStateByPercentageSchema = genericVehicleStateSchema.extend({\n currentChargePCT: percentageNumber,\n});\n\nconst electricVehicleStateByKwhSchema = genericVehicleStateSchema.extend({\n currentChargeInkWh: nonNegativeNumber,\n});\n\nconst electricVehicleStateSchema = z.union([electricVehicleStateByPercentageSchema, electricVehicleStateByKwhSchema]);\n\n// Charging preferences schemas\nconst chargingPreferencesPCTSchema = z.object({\n minChargeAtDestinationPCT: percentageNumber,\n minChargeAtChargingStopsPCT: z.number().min(0).max(50),\n});\n\nconst chargingPreferencesKWHSchema = z.object({\n minChargeAtDestinationInkWh: nonNegativeNumber,\n minChargeAtChargingStopsInkWh: nonNegativeNumber,\n});\n\nconst chargingPreferencesSchema = z.union([chargingPreferencesPCTSchema, chargingPreferencesKWHSchema]);\nconst electricVehiclePreferencesSchema = z.object({\n chargingPreferences: chargingPreferencesSchema.optional(),\n});\n\n// Vehicle restrictions schema\nconst vehicleRestrictionsSchema = z\n .object({\n maxSpeedKMH: z.number().min(0).max(250).optional(),\n tollTransponder: z.enum(tollTransponderOptions).optional(),\n })\n .optional();\n\n// Base vehicle parameters schema\nconst baseVehicleParamsSchema = {\n model: vehicleModelSchema,\n restrictions: vehicleRestrictionsSchema,\n};\n\n// Vehicle parameters schemas\nconst genericVehicleParamsSchema = z.object({\n ...baseVehicleParamsSchema,\n engineType: z.undefined().optional(),\n state: genericVehicleStateSchema.optional(),\n preferences: z.object({}).optional(),\n});\n\nconst combustionVehicleParamsSchema = z.object({\n ...baseVehicleParamsSchema,\n engineType: z.literal('combustion'),\n state: combustionVehicleStateSchema.optional(),\n preferences: z.object({}).optional(),\n});\n\nconst electricVehicleParamsSchema = z.object({\n ...baseVehicleParamsSchema,\n engineType: z.literal('electric'),\n state: electricVehicleStateSchema.optional(),\n preferences: electricVehiclePreferencesSchema.optional(),\n});\n\n/**\n * @ignore\n */\nexport const vehicleParametersSchema = z.union([\n z.discriminatedUnion('engineType', [combustionVehicleParamsSchema, electricVehicleParamsSchema]),\n genericVehicleParamsSchema,\n]);\n","import { avoidableTypes } from '@tomtom-org/maps-sdk/core';\nimport { z } from 'zod';\nimport { routeTypes } from '../types/commonRoutingParams';\nimport { hasBBoxSchema } from './geometriesSchema';\nimport { vehicleParametersSchema } from './vehicleParamsSchema';\n\n/**\n * @ignore\n */\nexport const commonRoutingRequestSchema = z.object({\n costModel: z\n .object({\n avoid: z.array(z.enum(avoidableTypes)).optional(),\n traffic: z.enum(['live', 'historical']).optional(),\n routeType: z.enum(routeTypes).optional(),\n avoidAreas: z.array(hasBBoxSchema).max(10).optional(),\n })\n .optional(),\n travelMode: z.string().optional(),\n vehicle: vehicleParametersSchema.optional(),\n when: z\n .object({\n option: z.enum(['departAt', 'arriveBy']),\n date: z.date(),\n })\n .optional(),\n});\n","import type { Avoidable, HasLngLat } from '@tomtom-org/maps-sdk/core';\nimport type {\n CombustionVehicleParams,\n CombustionVehicleState,\n CommonRoutingParams,\n CommonServiceParams,\n CostModel,\n DepartArriveParams,\n ElectricVehicleParams,\n ElectricVehicleState,\n GenericVehicleParams,\n VehicleParameters,\n} from '../../shared';\nimport type { VehicleRestrictions } from '../../shared/types/vehicleRestrictionParams';\nimport type { ReachableRangeRequestAPI } from './apiRequestTypes';\nimport type { ReachableRangeResponseAPI } from './apiResponseTypes';\n\nexport const budgetTypes = [\n 'timeMinutes',\n 'remainingChargeCPT',\n 'spentChargePCT',\n 'spentFuelLiters',\n 'distanceKM',\n] as const;\n\n/**\n * Type of budget constraint for reachable range calculation.\n *\n * @remarks\n * **Budget Types:**\n * - `timeMinutes`: Travel time in minutes (isochrone)\n * - `distanceKM`: Travel distance in kilometers (isodistance)\n * - `remainingChargeCPT`: Remaining battery charge in percentage (EV)\n * - `spentChargePCT`: Battery charge consumed in percentage (EV)\n * - `spentFuelLiters`: Fuel consumed in liters (combustion)\n *\n * **Common Use Cases:**\n * - Time: \"Where can I reach in 30 minutes?\"\n * - Distance: \"What's within 10 km?\"\n * - EV charge: \"How far can I go on 50% battery?\"\n * - Fuel: \"Range with 20 liters of fuel\"\n *\n * @example\n * ```typescript\n * const timeType: BudgetType = 'timeMinutes';\n * const distanceType: BudgetType = 'distanceKM';\n * const evType: BudgetType = 'remainingChargeCPT';\n * ```\n *\n * @group Reachable Range\n */\nexport type BudgetType = (typeof budgetTypes)[number];\n\n/**\n * Budget constraint for reachable range calculation.\n *\n * Defines the limit (time, distance, or fuel/charge) for calculating\n * how far you can travel from a starting point.\n *\n * @remarks\n * **Use Cases:**\n * - Service area visualization (30-min delivery zone)\n * - EV range anxiety mitigation (show reachable area)\n * - Emergency response coverage (10-min response time)\n * - Delivery zone planning\n * - Store location analysis\n *\n * @example\n * ```typescript\n * // 30-minute travel time\n * const timeBudget: ReachableRangeBudget = {\n * type: 'timeMinutes',\n * value: 30\n * };\n *\n * // 50 km distance\n * const distanceBudget: ReachableRangeBudget = {\n * type: 'distanceKM',\n * value: 50\n * };\n *\n * // 50% battery remaining\n * const evBudget: ReachableRangeBudget = {\n * type: 'remainingChargeCPT',\n * value: 50\n * };\n *\n * // 20 liters of fuel\n * const fuelBudget: ReachableRangeBudget = {\n * type: 'spentFuelLiters',\n * value: 20\n * };\n * ```\n *\n * @group Reachable Range\n */\nexport type ReachableRangeBudget = {\n /**\n * The type of budget, including units.\n *\n * @remarks\n * - `timeMinutes`: Minutes of travel time\n * - `distanceKM`: Kilometers of travel distance\n * - `remainingChargeCPT`: Battery percentage remaining\n * - `spentChargePCT`: Battery percentage consumed\n * - `spentFuelLiters`: Liters of fuel consumed\n */\n type: BudgetType;\n\n /**\n * The value of the budget based on the units mentioned in the type.\n *\n * @remarks\n * **Typical Values:**\n * - Time: 5-60 minutes\n * - Distance: 5-100 km\n * - Charge: 10-100 percentage\n * - Fuel: 5-50 liters\n *\n * @example\n * ```typescript\n * value: 30 // 30 minutes/km/percent/liters depending on type\n * value: 15.5 // Decimal values supported\n * ```\n */\n value: number;\n};\n\n/**\n * Route features `calculateReachableRange` can avoid.\n *\n * @remarks\n * Every {@link Avoidable} except `alreadyUsedRoads`, which this endpoint rejects with\n * *\"Avoid alreadyUsedRoads not supported for calculateReachableRange\"*. It stays available on\n * `calculateRoute`.\n *\n * @group Reachable Range\n */\nexport type ReachableRangeAvoidable = Exclude<Avoidable, 'alreadyUsedRoads'>;\n\n/**\n * Cost model accepted by `calculateReachableRange`.\n *\n * @remarks\n * {@link CostModel} without `avoidAreas`, which this endpoint has no parameter for, and with the\n * narrower {@link ReachableRangeAvoidable} avoid list.\n *\n * @group Reachable Range\n */\nexport type ReachableRangeCostModel = Omit<CostModel, 'avoid' | 'avoidAreas'> & {\n /**\n * Route features to avoid when calculating the range.\n */\n avoid?: ReachableRangeAvoidable[];\n};\n\n/**\n * Vehicle parameters accepted by `calculateReachableRange`.\n *\n * @remarks\n * {@link VehicleParameters} without `state.heading`, which this endpoint rejects with\n * *\"parameter [vehicleHeading] not supported\"*. It stays available on `calculateRoute`, where the\n * routing API takes it as `vehicleHeadingInDegrees`.\n *\n * Everything else is unchanged, including the engine-specific `state` variants -- an electric\n * vehicle still picks between the percentage and kWh forms.\n *\n * @group Reachable Range\n */\nexport type ReachableRangeVehicleParameters = (\n | Omit<GenericVehicleParams, 'state'>\n | (Omit<CombustionVehicleParams, 'state'> & { state?: CombustionVehicleState })\n | (Omit<ElectricVehicleParams, 'state'> & { state?: ElectricVehicleState })\n) &\n VehicleRestrictions;\n\n/**\n * Parameters specific to reachable range calculation.\n *\n * @remarks\n * These parameters are combined with common routing parameters to calculate\n * the reachable area from a starting point.\n *\n * @group Reachable Range\n */\nexport type ReachableRangeOwnParams = {\n /**\n * How the routes underlying the range are optimized, and what they avoid.\n *\n * @remarks\n * Narrower than {@link CostModel}: `calculateReachableRange` rejects `alreadyUsedRoads` and has\n * no avoid-areas parameter, so neither is offered here.\n */\n costModel?: ReachableRangeCostModel;\n\n /**\n * Parameters for the vehicle whose reachable range is calculated.\n *\n * @remarks\n * Narrower than {@link VehicleParameters}: `calculateReachableRange` has no vehicle-heading\n * parameter, so {@link ReachableRangeVehicleParameters} does not offer `state.heading`.\n */\n vehicle?: ReachableRangeVehicleParameters;\n\n /**\n * Location from which the range calculation should start.\n *\n * @remarks\n * The center point from which reachability is calculated. Can be:\n * - An object with `lon` and `lat` properties\n * - An array `[longitude, latitude]`\n *\n * @example\n * ```typescript\n * // Object format\n * origin: { lon: 4.9, lat: 52.3 }\n *\n * // Array format\n * origin: [4.9, 52.3]\n * ```\n */\n origin: HasLngLat;\n\n /**\n * The budget for the reachable range calculation.\n *\n * @remarks\n * Consists of a type indicating whether it's about time, distance, or\n * fuel/charge and the units, plus its value.\n *\n * Determines the extent of the reachable area polygon.\n */\n budget: ReachableRangeBudget;\n\n /**\n * Maximum ferry length in meters to consider.\n *\n * @remarks\n * Ferries longer than this value will be avoided in the calculation.\n * Useful for excluding long ferry routes that might extend the range\n * unrealistically.\n *\n * @example\n * ```typescript\n * maxFerryLengthMeters: 5000 // Avoid ferries longer than 5 km\n * ```\n */\n maxFerryLengthMeters?: number;\n\n /**\n * Applies post-processing to smooth the polygon boundary for better visual appearance.\n *\n * @remarks\n * Stronger smoothing generally results in more points.\n *\n * @default `'strong'`\n *\n * @example\n * ```typescript\n * smoothing: 'strong'\n * ```\n */\n smoothing?: 'none' | 'weak' | 'strong';\n\n /**\n * Specifies when to depart.\n *\n * @remarks\n * If past dates are supplied or dates that are impossible to achieve,\n * it will default to departing now.\n *\n * **Traffic Impact:**\n * - Future departure times use predictive traffic\n * - Current time uses live traffic\n * - Historic times use historical patterns\n *\n * @default Depart now\n *\n * @example\n * ```typescript\n * when: { departAt: new Date('2025-10-20T08:00:00Z') }\n * when: { departAt: 'now' }\n * ```\n */\n when?: DepartArriveParams<'departAt'>;\n};\n\n/**\n * Complete parameters for calculating a reachable range.\n *\n * Combines common service parameters, routing parameters, and reachable range\n * specific options to compute an isochrone or isodistance polygon.\n *\n * @remarks\n * **What it Returns:**\n * A polygon representing the area reachable from the origin within the\n * specified budget (time, distance, or fuel/charge).\n *\n * **Use Cases:**\n * - Delivery zone visualization\n * - Service area mapping\n * - Emergency response coverage\n * - EV range display\n * - Store catchment areas\n * - Real estate search (30-min commute)\n *\n * **Traffic Consideration:**\n * Results vary based on departure time and traffic conditions. Use\n * appropriate `when` values for accurate predictions.\n *\n * @example\n * ```typescript\n * // 30-minute driving range\n * const params: ReachableRangeParams = {\n * key: 'your-api-key',\n * origin: [4.9, 52.3],\n * budget: {\n * type: 'timeMinutes',\n * value: 30\n * },\n * routeType: 'fastest',\n * traffic: 'live',\n * when: { departAt: 'now' }\n * };\n *\n * // 50 km distance range\n * const distanceParams: ReachableRangeParams = {\n * key: 'your-api-key',\n * origin: [4.9, 52.3],\n * budget: {\n * type: 'distanceKM',\n * value: 50\n * }\n * };\n *\n * // EV range with 50% battery\n * const evParams: ReachableRangeParams = {\n * key: 'your-api-key',\n * origin: [4.9, 52.3],\n * budget: {\n * type: 'remainingChargeCPT',\n * value: 50\n * },\n * vehicle: {\n * engineType: 'electric',\n * model: {\n * engine: {\n * consumption: {\n * charging: { maxChargeKWH: 100 }\n * }\n * }\n * },\n * state: {\n * currentChargePCT: 80\n * }\n * }\n * };\n *\n * // Avoid toll roads\n * const noTollParams: ReachableRangeParams = {\n * key: 'your-api-key',\n * origin: [4.9, 52.3],\n * budget: {\n * type: 'timeMinutes',\n * value: 45\n * },\n * avoid: ['tollRoads']\n * };\n * ```\n *\n * @group Reachable Range\n */\nexport type ReachableRangeParams = CommonServiceParams<ReachableRangeRequestAPI, ReachableRangeResponseAPI> &\n Omit<CommonRoutingParams, 'costModel' | 'vehicle'> &\n ReachableRangeOwnParams;\n","import { avoidableTypes } from '@tomtom-org/maps-sdk/core';\nimport { z } from 'zod';\nimport { commonRoutingRequestSchema } from '../shared/schema/commonRoutingRequestSchema';\nimport { hasLngLatSchema } from '../shared/schema/geometriesSchema';\nimport { vehicleParametersSchema } from '../shared/schema/vehicleParamsSchema';\nimport { routeTypes } from '../shared/types/commonRoutingParams';\nimport { budgetTypes } from './types/reachableRangeParams';\n\n// import { SchemaRefinement } from \"../shared/types/validation\";\n\nconst reachableRangeRequestSchemaMandatory = z.object({\n origin: hasLngLatSchema,\n budget: z.object({\n type: z.enum(budgetTypes),\n value: z.number().min(0),\n }),\n});\n\n// Narrower than `commonRoutingRequestSchema.costModel`: the endpoint rejects\n// `avoid=alreadyUsedRoads` outright, and has no avoid-areas parameter at all.\nconst reachableRangeAvoidableSchema = z.enum(avoidableTypes).exclude(['alreadyUsedRoads']);\n\n// Matches `ReachableRangeVehicleParameters`: the endpoint answers\n// `400 parameter [vehicleHeading] not supported`, so a heading fails validation here rather than\n// being quietly dropped for callers who are not type-checked.\nconst reachableRangeVehicleSchema = vehicleParametersSchema.check(\n z.refine(\n (vehicle) => !(vehicle.state && 'heading' in vehicle.state),\n 'vehicle.state.heading: calculateReachableRange has no vehicle heading parameter',\n ),\n);\n\nconst reachableRangeRequestSchemaOptional = z.object({\n vehicle: reachableRangeVehicleSchema.optional(),\n costModel: z\n .object({\n avoid: z.array(reachableRangeAvoidableSchema).optional(),\n traffic: z.enum(['live', 'historical']).optional(),\n routeType: z.enum(routeTypes).optional(),\n })\n .optional(),\n maxFerryLengthMeters: z.number().min(0).optional(),\n smoothing: z.enum(['none', 'weak', 'strong']).optional(),\n});\n\nconst reachableRangeRequestSchema = commonRoutingRequestSchema.extend(\n reachableRangeRequestSchemaMandatory.extend(reachableRangeRequestSchemaOptional.shape).shape,\n);\n\n// const departArriveRefinement: SchemaRefinement<ReachableRangeParams> = {\n// check: (data: ReachableRangeParams): boolean => (data.when?.option as string) != \"arriveBy\",\n// message: \"When calculating a reachable range, departure date-time can be specified, but not arrival date-time\"\n// };\n\n// const evRangeRefinement: SchemaRefinement<ReachableRangeParams> = {\n// check: (data: ReachableRangeParams): boolean =>\n// !(\n// (data.budget.type === \"remainingChargeCPT\" || data.budget.type === \"spentChargePCT\") &&\n// data.vehicle?.engine?.type != \"electric\"\n// ),\n// message: \"With an EV reachable range, the vehicle parameters must be set, with 'electric' engine type\"\n// };\n//\n// const fuelRangeRefinement: SchemaRefinement<ReachableRangeParams> = {\n// check: (data: ReachableRangeParams): boolean =>\n// !(data.budget.type === \"spentFuelLiters\" && data.vehicle?.engine?.type != \"combustion\"),\n// message: \"With a fuel reachable range, the vehicle parameters must be set, with 'combustion' engine type\"\n// };\n\n/**\n * @ignore\n */\nexport const reachableRangeRequestValidationConfig = {\n schema: reachableRangeRequestSchema,\n // refinements: [evRangeRefinement, fuelRangeRefinement, departArriveRefinement]\n};\n","import { isNil } from 'lodash-es';\nimport {\n CombustionEngineModel,\n ConsumptionModelEfficiency,\n ElectricConsumptionModel,\n ElectricEngineModel,\n SpeedToConsumptionRate,\n VehicleEngineModel,\n VehicleEngineType,\n} from '../types/vehicleEngineParams';\nimport { VehicleDimensions } from '../types/vehicleModel';\nimport { ElectricVehicleParams, VehicleParameters } from '../types/vehicleParams';\nimport { ChargingPreferencesKWH, ChargingPreferencesPCT } from '../types/vehiclePreferences';\nimport { ElectricVehicleStateKWH, ElectricVehicleStatePCT, VehicleState } from '../types/vehicleState';\n\nconst appendConsumptionEfficiency = (urlParams: URLSearchParams, efficiency?: ConsumptionModelEfficiency): void => {\n if (efficiency) {\n !isNil(efficiency.acceleration) && urlParams.append('accelerationEfficiency', String(efficiency.acceleration));\n !isNil(efficiency.deceleration) && urlParams.append('decelerationEfficiency', String(efficiency.deceleration));\n !isNil(efficiency.uphill) && urlParams.append('uphillEfficiency', String(efficiency.uphill));\n !isNil(efficiency.downhill) && urlParams.append('downhillEfficiency', String(efficiency.downhill));\n }\n};\n\n// e.g. 50,6.3:130,11.5\nconst buildSpeedToConsumptionString = (speedsToConsumptions: SpeedToConsumptionRate[]): string =>\n speedsToConsumptions\n .map((speedToConsumption) => `${speedToConsumption.speedKMH},${speedToConsumption.consumptionUnitsPer100KM}`)\n .join(':');\n\nconst appendCombustionEngine = (urlParams: URLSearchParams, engine: CombustionEngineModel): void => {\n // (no need to append combustion vehicleEngineType since it's the default)\n const consumptionModel = engine.consumption;\n consumptionModel.speedsToConsumptionsLiters &&\n urlParams.append(\n 'constantSpeedConsumptionInLitersPerHundredkm',\n buildSpeedToConsumptionString(consumptionModel.speedsToConsumptionsLiters),\n );\n !isNil(consumptionModel.auxiliaryPowerInLitersPerHour) &&\n urlParams.append('auxiliaryPowerInLitersPerHour', String(consumptionModel.auxiliaryPowerInLitersPerHour));\n !isNil(consumptionModel.fuelEnergyDensityInMJoulesPerLiter) &&\n urlParams.append(\n 'fuelEnergyDensityInMJoulesPerLiter',\n String(consumptionModel.fuelEnergyDensityInMJoulesPerLiter),\n );\n};\n\nconst appendElectricConsumptionModel = (urlParams: URLSearchParams, model: ElectricConsumptionModel): void => {\n model.speedsToConsumptionsKWH &&\n urlParams.append(\n 'constantSpeedConsumptionInkWhPerHundredkm',\n buildSpeedToConsumptionString(model.speedsToConsumptionsKWH),\n );\n !isNil(model.auxiliaryPowerInkW) && urlParams.append('auxiliaryPowerInkW', String(model.auxiliaryPowerInkW));\n !isNil(model.consumptionInKWHPerKMAltitudeGain) &&\n urlParams.append('consumptionInkWhPerkmAltitudeGain', String(model.consumptionInKWHPerKMAltitudeGain));\n !isNil(model.recuperationInKWHPerKMAltitudeLoss) &&\n urlParams.append('recuperationInkWhPerkmAltitudeLoss', String(model.recuperationInKWHPerKMAltitudeLoss));\n};\n\nconst appendChargingModel = (urlParams: URLSearchParams, engine: ElectricEngineModel): void => {\n const chargingModel = engine.charging;\n if (chargingModel?.maxChargeKWH) {\n urlParams.append('maxChargeInkWh', String(chargingModel.maxChargeKWH));\n }\n // (the rest of the charging model goes as POST data)\n};\n\nconst appendVehicleState = (urlParams: URLSearchParams, vehicleParams: VehicleParameters): void => {\n if (!vehicleParams.state) {\n return;\n }\n\n if (!('engineType' in vehicleParams)) {\n // Generic vehicle, no engine-specific state to append:\n return;\n }\n\n // Engine-specific state props:\n if (vehicleParams.engineType === 'combustion') {\n const combustionState: VehicleState<'combustion'> = vehicleParams.state;\n combustionState.currentFuelInLiters &&\n urlParams.append('currentFuelInLiters', String(combustionState.currentFuelInLiters));\n } else if (vehicleParams.engineType === 'electric') {\n const electricState: VehicleState<'electric'> = vehicleParams.state;\n const kwhElectricState = electricState as ElectricVehicleStateKWH;\n const pctElectricState = electricState as ElectricVehicleStatePCT;\n\n if (kwhElectricState.currentChargeInkWh) {\n urlParams.append('currentChargeInkWh', String(kwhElectricState.currentChargeInkWh));\n } else if (\n pctElectricState.currentChargePCT &&\n vehicleParams.model &&\n 'engine' in vehicleParams.model &&\n vehicleParams.model.engine\n ) {\n const engine = vehicleParams.model.engine;\n const maxChargeKWH = engine.charging?.maxChargeKWH;\n if (maxChargeKWH) {\n // currentChargePCT needs maxChargeKWH to be converted to kWh\n urlParams.append(\n 'currentChargeInkWh',\n String((maxChargeKWH * pctElectricState.currentChargePCT) / 100),\n );\n }\n }\n }\n};\n\nconst appendVehiclePreferences = (urlParams: URLSearchParams, vehicleParams: VehicleParameters): void => {\n if (!vehicleParams.preferences) {\n return;\n }\n\n if ('engineType' in vehicleParams && vehicleParams.engineType === 'electric') {\n const preferences = vehicleParams.preferences;\n if (preferences.chargingPreferences) {\n const chargingPrefs = preferences.chargingPreferences;\n const kwhChargingPrefs = chargingPrefs as ChargingPreferencesKWH;\n const pctChargingPrefs = chargingPrefs as ChargingPreferencesPCT;\n\n // Check if absolute kWh values are available and use them directly\n if (kwhChargingPrefs.minChargeAtChargingStopsInkWh || kwhChargingPrefs.minChargeAtDestinationInkWh) {\n urlParams.append('minChargeAtDestinationInkWh', String(kwhChargingPrefs.minChargeAtDestinationInkWh));\n urlParams.append(\n 'minChargeAtChargingStopsInkWh',\n String(kwhChargingPrefs.minChargeAtChargingStopsInkWh),\n );\n } else if (\n (pctChargingPrefs.minChargeAtChargingStopsPCT || pctChargingPrefs.minChargeAtDestinationPCT) &&\n vehicleParams.model &&\n 'engine' in vehicleParams.model &&\n vehicleParams.model.engine\n ) {\n // Considering percentage values if absolute values not available and maxChargeKWH exists\n const engine = vehicleParams.model.engine;\n const maxChargeKWH = engine.charging?.maxChargeKWH;\n if (maxChargeKWH) {\n urlParams.append(\n 'minChargeAtDestinationInkWh',\n String((maxChargeKWH * pctChargingPrefs.minChargeAtDestinationPCT) / 100),\n );\n urlParams.append(\n 'minChargeAtChargingStopsInkWh',\n String((maxChargeKWH * pctChargingPrefs.minChargeAtChargingStopsPCT) / 100),\n );\n }\n }\n }\n }\n};\n\nconst appendVehicleDimensions = (urlParams: URLSearchParams, dimensions?: VehicleDimensions): void => {\n // (default is 0):\n if (dimensions?.weightKG) urlParams.append('vehicleWeight', String(dimensions.weightKG));\n};\n\nconst appendVehicleRestrictions = (urlParams: URLSearchParams, vehicleParams: VehicleParameters): void => {\n // (default is 0):\n const maxSpeedKMH = vehicleParams.restrictions?.maxSpeedKMH;\n if (maxSpeedKMH) urlParams.append('vehicleMaxSpeed', String(maxSpeedKMH));\n\n // This V2 endpoint spells it \"Electric\"; the V3 routing endpoints spell it \"Electronic\" and\n // take it in the POST body. Each rejects the other's name.\n const tollTransponder = vehicleParams.restrictions?.tollTransponder;\n if (tollTransponder) urlParams.append('vehicleHasElectricTollCollectionTransponder', tollTransponder);\n};\n\nconst appendVehicleEngineModel = (\n urlParams: URLSearchParams,\n engineType: VehicleEngineType,\n engine: VehicleEngineModel<VehicleEngineType>,\n): void => {\n // (efficiency params have the same names between engine types)\n appendConsumptionEfficiency(urlParams, engine.consumption.efficiency);\n\n if (engineType === 'electric') {\n appendElectricConsumptionModel(urlParams, (engine as ElectricEngineModel).consumption);\n appendChargingModel(urlParams, engine as ElectricEngineModel);\n } else {\n // (no need to append combustion vehicleEngineType since it's the default)\n appendCombustionEngine(urlParams, engine as CombustionEngineModel);\n }\n};\n\nconst appendVehicleModel = (urlParams: URLSearchParams, vehicleParams: VehicleParameters): void => {\n if (!vehicleParams.model) {\n return;\n }\n\n // Handle predefined vehicle model\n if ('variantId' in vehicleParams.model) {\n urlParams.append('vehicleModelId', vehicleParams.model.variantId);\n } else {\n // Handle explicit vehicle model (dimensions and engine)\n appendVehicleDimensions(urlParams, vehicleParams.model.dimensions);\n\n if (vehicleParams.model.engine) {\n appendVehicleEngineModel(\n urlParams,\n 'engineType' in vehicleParams ? vehicleParams.engineType : undefined,\n vehicleParams.model.engine,\n );\n }\n }\n};\n\n/**\n * Appends vehicle parameters to the URL search params for routing requests.\n * @param urlParams - The URLSearchParams to append to\n * @param vehicleParams - The vehicle parameters to append\n */\nexport const appendVehicleParams = (urlParams: URLSearchParams, vehicleParams?: VehicleParameters): void => {\n if (!vehicleParams) {\n return;\n }\n\n // the engine type defaults to combustion, thus we only bother to append it if it's electric:\n if ((vehicleParams as ElectricVehicleParams).engineType === 'electric') {\n urlParams.append('vehicleEngineType', 'electric');\n }\n\n appendVehicleModel(urlParams, vehicleParams);\n appendVehicleState(urlParams, vehicleParams);\n appendVehiclePreferences(urlParams, vehicleParams);\n appendVehicleRestrictions(urlParams, vehicleParams);\n};\n","import type { CommonRoutingParams, DepartArriveParams } from '../types/commonRoutingParams';\nimport { appendByRepeatingParamName, appendOptionalParam } from './requestBuildingUtils';\nimport { appendVehicleParams } from './routingVehicleParamsBuilder';\n\nconst appendWhenParams = (urlParams: URLSearchParams, when?: DepartArriveParams): void => {\n if (when?.date) {\n const formattedDate = when.date.toISOString();\n if (when.option === 'departAt') {\n urlParams.append('departAt', formattedDate);\n } else if (when.option === 'arriveBy') {\n urlParams.append('arriveAt', formattedDate);\n }\n }\n};\n\n/**\n * @ignore\n */\nexport const appendCommonRoutingParams = (urlParams: URLSearchParams, params: CommonRoutingParams): void => {\n const costModel = params.costModel;\n appendByRepeatingParamName(urlParams, 'avoid', costModel?.avoid);\n appendOptionalParam(urlParams, 'traffic', costModel?.traffic);\n appendWhenParams(urlParams, params.when);\n appendOptionalParam(urlParams, 'routeType', costModel?.routeType);\n appendOptionalParam(urlParams, 'travelMode', params.travelMode);\n appendVehicleParams(urlParams, params.vehicle);\n};\n","import { getPositionStrict } from '@tomtom-org/maps-sdk/core';\nimport type { ElectricVehicleParams, ElectricVehicleStatePCT, FetchInput } from '../shared';\nimport { appendCommonRoutingParams } from '../shared/request/commonRoutingRequestBuilder';\nimport { appendCommonParams, appendOptionalParam } from '../shared/request/requestBuildingUtils';\nimport type { ReachableRangeParams } from './types/reachableRangeParams';\n\nconst buildUrlBasePath = (params: ReachableRangeParams, latLon: string): string =>\n params.customServiceBaseURL ?? `${params.commonBaseURL}/maps/orbis/routing/calculateReachableRange/${latLon}/json`;\n\nconst getMaxChargeKWH = (params: ReachableRangeParams): number | undefined => {\n const vehicle = params.vehicle as ElectricVehicleParams | undefined;\n if (!vehicle?.model || !('engine' in vehicle.model)) {\n return undefined;\n }\n return vehicle.model.engine?.charging?.maxChargeKWH;\n};\n\nconst getCurrentChargeKWH = (params: ReachableRangeParams): number | undefined => {\n const vehicle = params.vehicle as ElectricVehicleParams | undefined;\n if (!vehicle?.state) {\n return undefined;\n }\n const pctState = vehicle.state as ElectricVehicleStatePCT;\n if (pctState.currentChargePCT != null) {\n const maxChargeKWH = getMaxChargeKWH(params);\n if (maxChargeKWH != null) {\n return (maxChargeKWH * pctState.currentChargePCT) / 100;\n }\n }\n return undefined;\n};\n\nconst appendBudget = (urlParams: URLSearchParams, params: ReachableRangeParams): void => {\n const budget = params.budget;\n switch (budget.type) {\n case 'timeMinutes':\n urlParams.append('timeBudgetInSec', (budget.value * 60).toString());\n break;\n case 'distanceKM':\n urlParams.append('distanceBudgetInMeters', (budget.value * 1000).toString());\n break;\n case 'spentFuelLiters':\n urlParams.append('fuelBudgetInLiters', budget.value.toString());\n break;\n case 'spentChargePCT': {\n const maxChargeKWH = getMaxChargeKWH(params);\n if (maxChargeKWH != null) {\n urlParams.append('energyBudgetInkWh', ((maxChargeKWH * budget.value) / 100).toString());\n }\n break;\n }\n case 'remainingChargeCPT': {\n const currentChargeKWH = getCurrentChargeKWH(params);\n const maxChargeKWH = getMaxChargeKWH(params);\n if (maxChargeKWH != null && currentChargeKWH != null) {\n const remainingKWH = (maxChargeKWH * budget.value) / 100;\n urlParams.append('energyBudgetInkWh', Math.max(0, currentChargeKWH - remainingKWH).toString());\n }\n break;\n }\n default:\n // Unsupported by SDK but will attempt to send it anyway\n urlParams.append(budget.type, budget.value.toString());\n }\n};\n\nconst buildLatLon = (params: ReachableRangeParams): string => {\n const position = getPositionStrict(params.origin);\n return `${position[1]},${position[0]}`;\n};\n\n/**\n * @param params\n * @returns\n */\nexport const buildReachableRangeRequest = (params: ReachableRangeParams): FetchInput => {\n const latLon = buildLatLon(params);\n const url = new URL(buildUrlBasePath(params, latLon));\n const urlParams = url.searchParams;\n appendCommonParams(urlParams, params);\n // The reachable range API does not support language\n urlParams.delete('language');\n appendCommonRoutingParams(urlParams, params);\n appendBudget(urlParams, params);\n appendOptionalParam(urlParams, 'maxFerryLengthInMeters', params.maxFerryLengthMeters);\n appendOptionalParam(urlParams, 'smoothing', params.smoothing ?? 'strong');\n return { method: 'GET', url };\n};\n","import type { BBox, PolygonFeature } from '@tomtom-org/maps-sdk/core';\nimport { bboxFromGeoJSON } from '@tomtom-org/maps-sdk/core';\nimport type { Polygon } from 'geojson';\nimport type { ReachableRangeResponseAPI } from './types/apiResponseTypes';\nimport type { ReachableRangeParams } from './types/reachableRangeParams';\n\n/**\n *\n * @param apiResponse\n * @param params\n */\nexport const parseReachableRangeResponse = (\n apiResponse: ReachableRangeResponseAPI,\n params: ReachableRangeParams,\n): PolygonFeature<ReachableRangeParams> => {\n const geometry: Polygon = {\n type: 'Polygon',\n coordinates: [apiResponse.reachableRange.boundary.map((point) => [point.longitude, point.latitude])],\n };\n const bbox = bboxFromGeoJSON(geometry) as BBox; // the geometry should always have a bbox\n return { type: 'Feature', geometry, bbox, properties: params };\n};\n","import type { PolygonFeature } from '@tomtom-org/maps-sdk/core';\nimport { parseRoutingResponseError } from '../routing/routingResponseErrorParser';\nimport type { ServiceTemplate } from '../shared';\nimport { fetchWith } from '../shared/fetch';\nimport { reachableRangeRequestValidationConfig } from './reachableRangeRequestSchema';\nimport { buildReachableRangeRequest } from './requestBuilder';\nimport { parseReachableRangeResponse } from './responseParser';\nimport type { ReachableRangeRequestAPI } from './types/apiRequestTypes';\nimport type { ReachableRangeResponseAPI } from './types/apiResponseTypes';\nimport type { ReachableRangeParams } from './types/reachableRangeParams';\n\nexport type ReachableRangeTemplate = ServiceTemplate<\n ReachableRangeParams,\n ReachableRangeRequestAPI,\n ReachableRangeResponseAPI,\n PolygonFeature<ReachableRangeParams>\n>;\n\nexport const reachableRangeTemplate: ReachableRangeTemplate = {\n requestValidation: reachableRangeRequestValidationConfig,\n buildRequest: buildReachableRangeRequest,\n sendRequest: fetchWith,\n parseResponse: parseReachableRangeResponse,\n parseResponseError: parseRoutingResponseError,\n getAPIVersion: () => 2,\n};\n","import type { ReachableRangeTemplate } from './reachableRangeTemplate';\nimport { reachableRangeTemplate } from './reachableRangeTemplate';\nimport { buildReachableRangeRequest } from './requestBuilder';\nimport { parseReachableRangeResponse } from './responseParser';\n\nconst customize: {\n buildReachableRangeRequest: typeof buildReachableRangeRequest;\n parseReachableRangeResponse: typeof parseReachableRangeResponse;\n reachableRangeTemplate: ReachableRangeTemplate;\n} = {\n buildReachableRangeRequest,\n parseReachableRangeResponse,\n reachableRangeTemplate,\n};\nexport default customize;\n","import type { GeographyType } from '@tomtom-org/maps-sdk/core';\nimport { getPositionStrict } from '@tomtom-org/maps-sdk/core';\nimport { isNil } from 'lodash-es';\nimport type { GetObject } from '../shared';\nimport { PLACES_URL_PATH } from '../shared/request/commonSearchRequestBuilder';\nimport { buildCommonServiceRequestHeaders } from '../shared/request/requestBuildingUtils';\nimport type { ReverseGeocodingAreaTypeAPI } from './types/apiTypes';\nimport type { ReverseGeocodingParams } from './types/reverseGeocodingParams';\n\n// Maps the SDK's public GeographyType values to the API's `areaTypes` values.\nexport const AREA_TYPE_BY_GEOGRAPHY_TYPE: Record<GeographyType, ReverseGeocodingAreaTypeAPI> = {\n Country: 'country',\n CountrySubdivision: 'countrySubdivision',\n CountrySecondarySubdivision: 'countrySecondarySubdivision',\n CountryTertiarySubdivision: 'countryTertiarySubdivision',\n Municipality: 'municipality',\n MunicipalitySubdivision: 'municipalitySubdivision',\n MunicipalitySecondarySubdivision: 'municipalitySecondarySubdivision',\n Neighbourhood: 'neighborhood',\n PostalCodeArea: 'postalCode',\n};\n\n// Requests every result field the SDK's response parser understands, so callers get a\n// complete place without having to specify a projection themselves.\nconst DEFAULT_ATTRIBUTES = 'results(*)';\n\nconst buildUrlBasePath = (params: ReverseGeocodingParams): string =>\n params.customServiceBaseURL ?? `${params.commonBaseURL}${PLACES_URL_PATH}/reverseGeocode`;\n\n/**\n * Default function for building a reverse geocoding request from {@link ReverseGeocodingParams}\n * @param params The reverse geocoding parameters, with global configuration already merged into them.\n */\nexport const buildRevGeoRequest = (params: ReverseGeocodingParams): GetObject => {\n const [lng, lat] = getPositionStrict(params.position);\n const url = new URL(buildUrlBasePath(params));\n const urlParams = url.searchParams;\n urlParams.append('position', `${lng},${lat}`);\n\n !isNil(params.radiusMeters) && urlParams.append('radiusInMeters', String(params.radiusMeters));\n params.geographyType &&\n urlParams.append(\n 'areaTypes',\n params.geographyType.map((geographyType) => AREA_TYPE_BY_GEOGRAPHY_TYPE[geographyType]).join(','),\n );\n !isNil(params.heading) && urlParams.append('vehicleHeadingInDegrees', String(params.heading));\n params.view && urlParams.append('geopoliticalView', params.view);\n\n return {\n url,\n headers: {\n ...buildCommonServiceRequestHeaders(params),\n Attributes: DEFAULT_ATTRIBUTES,\n },\n };\n};\n","const ISO2_TO_ISO3: Record<string, string> = {\n AF: 'AFG',\n AX: 'ALA',\n AL: 'ALB',\n DZ: 'DZA',\n AS: 'ASM',\n AD: 'AND',\n AO: 'AGO',\n AI: 'AIA',\n AQ: 'ATA',\n AG: 'ATG',\n AR: 'ARG',\n AM: 'ARM',\n AW: 'ABW',\n AU: 'AUS',\n AT: 'AUT',\n AZ: 'AZE',\n BS: 'BHS',\n BH: 'BHR',\n BD: 'BGD',\n BB: 'BRB',\n BY: 'BLR',\n BE: 'BEL',\n BZ: 'BLZ',\n BJ: 'BEN',\n BM: 'BMU',\n BT: 'BTN',\n BO: 'BOL',\n BQ: 'BES',\n BA: 'BIH',\n BW: 'BWA',\n BV: 'BVT',\n BR: 'BRA',\n IO: 'IOT',\n BN: 'BRN',\n BG: 'BGR',\n BF: 'BFA',\n BI: 'BDI',\n CV: 'CPV',\n KH: 'KHM',\n CM: 'CMR',\n CA: 'CAN',\n KY: 'CYM',\n CF: 'CAF',\n TD: 'TCD',\n CL: 'CHL',\n CN: 'CHN',\n CX: 'CXR',\n CC: 'CCK',\n CO: 'COL',\n KM: 'COM',\n CG: 'COG',\n CD: 'COD',\n CK: 'COK',\n CR: 'CRI',\n CI: 'CIV',\n HR: 'HRV',\n CU: 'CUB',\n CW: 'CUW',\n CY: 'CYP',\n CZ: 'CZE',\n DK: 'DNK',\n DJ: 'DJI',\n DM: 'DMA',\n DO: 'DOM',\n EC: 'ECU',\n EG: 'EGY',\n SV: 'SLV',\n GQ: 'GNQ',\n ER: 'ERI',\n EE: 'EST',\n SZ: 'SWZ',\n ET: 'ETH',\n FK: 'FLK',\n FO: 'FRO',\n FJ: 'FJI',\n FI: 'FIN',\n FR: 'FRA',\n GF: 'GUF',\n PF: 'PYF',\n TF: 'ATF',\n GA: 'GAB',\n GM: 'GMB',\n GE: 'GEO',\n DE: 'DEU',\n GH: 'GHA',\n GI: 'GIB',\n GR: 'GRC',\n GL: 'GRL',\n GD: 'GRD',\n GP: 'GLP',\n GU: 'GUM',\n GT: 'GTM',\n GG: 'GGY',\n GN: 'GIN',\n GW: 'GNB',\n GY: 'GUY',\n HT: 'HTI',\n HM: 'HMD',\n VA: 'VAT',\n HN: 'HND',\n HK: 'HKG',\n HU: 'HUN',\n IS: 'ISL',\n IN: 'IND',\n ID: 'IDN',\n IR: 'IRN',\n IQ: 'IRQ',\n IE: 'IRL',\n IM: 'IMN',\n IL: 'ISR',\n IT: 'ITA',\n JM: 'JAM',\n JP: 'JPN',\n JE: 'JEY',\n JO: 'JOR',\n KZ: 'KAZ',\n KE: 'KEN',\n KI: 'KIR',\n KP: 'PRK',\n KR: 'KOR',\n KW: 'KWT',\n KG: 'KGZ',\n LA: 'LAO',\n LV: 'LVA',\n LB: 'LBN',\n LS: 'LSO',\n LR: 'LBR',\n LY: 'LBY',\n LI: 'LIE',\n LT: 'LTU',\n LU: 'LUX',\n MO: 'MAC',\n MG: 'MDG',\n MW: 'MWI',\n MY: 'MYS',\n MV: 'MDV',\n ML: 'MLI',\n MT: 'MLT',\n MH: 'MHL',\n MQ: 'MTQ',\n MR: 'MRT',\n MU: 'MUS',\n YT: 'MYT',\n MX: 'MEX',\n FM: 'FSM',\n MD: 'MDA',\n MC: 'MCO',\n MN: 'MNG',\n ME: 'MNE',\n MS: 'MSR',\n MA: 'MAR',\n MZ: 'MOZ',\n MM: 'MMR',\n NA: 'NAM',\n NR: 'NRU',\n NP: 'NPL',\n NL: 'NLD',\n NC: 'NCL',\n NZ: 'NZL',\n NI: 'NIC',\n NE: 'NER',\n NG: 'NGA',\n NU: 'NIU',\n NF: 'NFK',\n MK: 'MKD',\n MP: 'MNP',\n NO: 'NOR',\n OM: 'OMN',\n PK: 'PAK',\n PW: 'PLW',\n PS: 'PSE',\n PA: 'PAN',\n PG: 'PNG',\n PY: 'PRY',\n PE: 'PER',\n PH: 'PHL',\n PN: 'PCN',\n PL: 'POL',\n PT: 'PRT',\n PR: 'PRI',\n QA: 'QAT',\n RE: 'REU',\n RO: 'ROU',\n RU: 'RUS',\n RW: 'RWA',\n BL: 'BLM',\n SH: 'SHN',\n KN: 'KNA',\n LC: 'LCA',\n MF: 'MAF',\n PM: 'SPM',\n VC: 'VCT',\n WS: 'WSM',\n SM: 'SMR',\n ST: 'STP',\n SA: 'SAU',\n SN: 'SEN',\n RS: 'SRB',\n SC: 'SYC',\n SL: 'SLE',\n SG: 'SGP',\n SX: 'SXM',\n SK: 'SVK',\n SI: 'SVN',\n SB: 'SLB',\n SO: 'SOM',\n ZA: 'ZAF',\n GS: 'SGS',\n SS: 'SSD',\n ES: 'ESP',\n LK: 'LKA',\n SD: 'SDN',\n SR: 'SUR',\n SJ: 'SJM',\n SE: 'SWE',\n CH: 'CHE',\n SY: 'SYR',\n TW: 'TWN',\n TJ: 'TJK',\n TZ: 'TZA',\n TH: 'THA',\n TL: 'TLS',\n TG: 'TGO',\n TK: 'TKL',\n TO: 'TON',\n TT: 'TTO',\n TN: 'TUN',\n TR: 'TUR',\n TM: 'TKM',\n TC: 'TCA',\n TV: 'TUV',\n UG: 'UGA',\n UA: 'UKR',\n AE: 'ARE',\n GB: 'GBR',\n US: 'USA',\n UM: 'UMI',\n UY: 'URY',\n UZ: 'UZB',\n VU: 'VUT',\n VE: 'VEN',\n VN: 'VNM',\n VG: 'VGB',\n VI: 'VIR',\n WF: 'WLF',\n EH: 'ESH',\n YE: 'YEM',\n ZM: 'ZMB',\n ZW: 'ZWE',\n};\n\nexport const toIso3 = (iso2: string): string => ISO2_TO_ISO3[iso2] ?? iso2;\n","import type { GeographyType } from '@tomtom-org/maps-sdk/core';\nimport { generateId, getPositionStrict, toPointGeometry } from '@tomtom-org/maps-sdk/core';\nimport { toIso3 } from '../shared/iso2ToIso3';\nimport { AREA_TYPE_BY_GEOGRAPHY_TYPE } from './requestBuilder';\nimport type { ReverseGeocodingResponse } from './reverseGeocoding';\nimport type {\n ReverseGeocodingAddressAPI,\n ReverseGeocodingAreaTypeAPI,\n ReverseGeocodingResponseAPI,\n ReverseGeocodingResultTypeAPI,\n} from './types/apiTypes';\nimport type { ReverseGeocodingParams } from './types/reverseGeocodingParams';\n\nconst PLACE_TYPE_BY_RESULT_TYPE: Record<ReverseGeocodingResultTypeAPI, 'Street' | 'Geography' | 'Point Address'> = {\n street: 'Street',\n area: 'Geography',\n address: 'Point Address',\n};\n\nconst GEOGRAPHY_TYPE_BY_AREA_TYPE = Object.fromEntries(\n Object.entries(AREA_TYPE_BY_GEOGRAPHY_TYPE).map(([geographyType, areaType]) => [areaType, geographyType]),\n) as Record<ReverseGeocodingAreaTypeAPI, GeographyType>;\n\n// Renames the handful of v2 address fields whose SDK-facing name differs from the API's, and\n// derives `countryCodeISO3`, which v2 does not return but the v1 search services do. Same\n// derivation the routing response parser applies to its own `countryCodeIso2` fields.\nconst mapAddress = (apiAddress: ReverseGeocodingAddressAPI) => {\n const { street, houseNumber, countryCodeIso2, ...rest } = apiAddress;\n return {\n ...rest,\n ...(street && { streetName: street }),\n ...(houseNumber && { streetNumber: houseNumber }),\n ...(countryCodeIso2 && { countryCode: countryCodeIso2, countryCodeISO3: toIso3(countryCodeIso2) }),\n };\n};\n\n/**\n * Default method for parsing reverse geocoding request from {@link ReverseGeocodingResponse}\n * @param params\n * @param apiResponse\n */\nexport const parseRevGeoResponse = (\n apiResponse: ReverseGeocodingResponseAPI,\n params: ReverseGeocodingParams,\n): ReverseGeocodingResponse => {\n const firstResult = apiResponse.results[0];\n return {\n type: 'Feature',\n // The requested coordinates are the primary ones, and set as the GeoJSON Feature geometry:\n geometry: toPointGeometry(getPositionStrict(params.position)),\n id: firstResult?.id ?? generateId(),\n ...(firstResult && {\n properties: {\n type: PLACE_TYPE_BY_RESULT_TYPE[firstResult.type],\n address: { freeformAddress: firstResult.title, ...mapAddress(firstResult.address) },\n ...(firstResult.areaType && {\n geographyType: [GEOGRAPHY_TYPE_BY_AREA_TYPE[firstResult.areaType]],\n // v2 has no `dataSources`, but for an area result its `id` is the geometry ID.\n // Surfacing it here keeps `getGeometryData` / `GeometriesModule` reachable from a\n // reverse geocoded geography, as it is from a search result.\n dataSources: { geometry: { id: firstResult.id } },\n }),\n ...(firstResult.accessPoints?.length && {\n accessPoints: firstResult.accessPoints.map((accessPoint) => accessPoint.position.coordinates),\n }),\n // The reverse geocoded coordinates are secondary and set in the GeoJSON properties:\n originalPosition: firstResult.position.coordinates,\n },\n }),\n };\n};\n","import { geographyTypes, views } from '@tomtom-org/maps-sdk/core';\nimport { z } from 'zod';\nimport { hasLngLatSchema } from '../shared/schema/geometriesSchema';\n\nconst revGeocodeRequestMandatory = z.object({\n position: hasLngLatSchema,\n});\n\nconst revGeocodeRequestOptional = z.object({\n geographyType: z.array(z.enum(geographyTypes)).optional(),\n heading: z.number().min(-360).max(360).optional(),\n radiusMeters: z.number().positive().max(5_000_000).optional(),\n view: z.enum(views).optional(),\n});\n\n/**\n * @ignore\n */\nexport const revGeocodeRequestSchema = revGeocodeRequestMandatory.extend(revGeocodeRequestOptional.shape);\n","import type { GetObject, ServiceTemplate } from '../shared';\nimport { get } from '../shared/fetch';\nimport { buildRevGeoRequest } from './requestBuilder';\nimport { parseRevGeoResponse } from './responseParser';\nimport type { ReverseGeocodingResponse } from './reverseGeocoding';\nimport { revGeocodeRequestSchema } from './revGeocodeRequestSchema';\nimport type { ReverseGeocodingResponseAPI } from './types/apiTypes';\nimport type { ReverseGeocodingParams } from './types/reverseGeocodingParams';\n\n/**\n * Reverse Geocoding service template type.\n * @ignore\n */\nexport type ReverseGeocodingTemplate = ServiceTemplate<\n ReverseGeocodingParams,\n GetObject,\n ReverseGeocodingResponseAPI,\n ReverseGeocodingResponse\n>;\n\n/**\n * Reverse Geocoding service template main implementation.\n * @ignore\n */\nexport const reverseGeocodingTemplate: ReverseGeocodingTemplate = {\n requestValidation: { schema: revGeocodeRequestSchema },\n buildRequest: buildRevGeoRequest,\n getAPIVersion: () => 2,\n sendRequest: get,\n parseResponse: parseRevGeoResponse,\n};\n","import { buildRevGeoRequest } from './requestBuilder';\nimport { parseRevGeoResponse } from './responseParser';\nimport type { ReverseGeocodingTemplate } from './reverseGeocodingTemplate';\nimport { reverseGeocodingTemplate } from './reverseGeocodingTemplate';\n\nconst customize: {\n buildRevGeoRequest: typeof buildRevGeoRequest;\n parseRevGeoResponse: typeof parseRevGeoResponse;\n reverseGeocodingTemplate: ReverseGeocodingTemplate;\n} = {\n buildRevGeoRequest,\n parseRevGeoResponse,\n reverseGeocodingTemplate,\n};\nexport default customize;\n","import type { GetPositionEntryPointOption, inputSectionTypes, RoutePlanningLocation } from '@tomtom-org/maps-sdk/core';\nimport type { CommonRoutingParams, CommonServiceParams } from '../../shared';\nimport type { CalculateRouteRequestAPI } from './apiRequestTypes';\nimport type { CalculateRouteResponseAPI } from './apiResponseTypes';\n\n/**\n * Route section type that can be requested in routing parameters.\n *\n * @remarks\n * Note: Some section types (like \"leg\") are automatically included regardless of this parameter.\n *\n * @group Routing\n */\nexport type InputSectionType = (typeof inputSectionTypes)[number];\n\n/**\n * Array of section types to include in the route response.\n *\n * Sections divide the route into portions with specific characteristics, helping you\n * display additional context like toll roads, ferry crossings, or traffic incidents.\n *\n * @remarks\n * Available section types:\n * - `carTrain`: Sections requiring car train transport\n * - `ferry`: Water crossings requiring ferry\n * - `tunnel`: Underground tunnel sections\n * - `motorway`: Highway/freeway sections\n * - `pedestrian`: Pedestrian-only sections\n * - `toll`: Sections requiring toll payment\n * - `tollVignette`: Sections requiring toll vignette\n * - `country`: Different countries traversed\n * - `traffic`: Sections with traffic incidents\n * - `vehicleRestricted`: Sections with vehicle restrictions\n * - `carpool`: HOV/carpool lane sections\n * - `urban`: Urban area sections\n * - `unpaved`: Unpaved road sections\n * - `lowEmissionZone`: Low emission zones\n * - `speedLimit`: Speed limit changes\n * - `roadShields`: Road shield information\n * - `importantRoadStretch`: Named important road stretches along the route\n *\n * @example\n * ```typescript\n * // Request toll, ferry, and traffic sections\n * const sectionTypes: InputSectionTypes = ['toll', 'ferry', 'traffic'];\n *\n * // Request all available sections\n * const sectionTypes: InputSectionTypes = inputSectionTypes;\n *\n * // Request no optional sections (leg sections still included)\n * const sectionTypes: InputSectionTypes = [];\n * ```\n *\n * @remarks\n * A requested section type is only present in the response if the route actually traverses it\n * (e.g. `ferry` is omitted on a route with no water crossing). The `lanes` section is guidance-only:\n * it is returned solely when {@link GuidanceParams | `guidance`} is also requested, and is therefore\n * not part of this input list.\n *\n * @default When omitted, all section types are requested (equivalent to `inputSectionTypes`).\n *\n * @group Routing\n */\nexport type InputSectionTypes = InputSectionType[];\n\n/**\n * Configuration for turn-by-turn guidance instructions.\n *\n * Specifies the format and detail level for navigation guidance.\n *\n * @example\n * ```typescript\n * // Request coded guidance with phonetics\n * const guidance: GuidanceParams = {\n * type: 'coded',\n * phonetics: 'IPA'\n * };\n * ```\n *\n * @group Routing\n */\nexport type GuidanceParams = {\n /**\n * Guidance instruction format type.\n *\n * Currently only 'coded' format is supported.\n */\n type: 'coded';\n /**\n * Phonetic transcription format for street names.\n *\n * @remarks\n * - `LHP`: Language-specific phonetic representation\n * - `IPA`: International Phonetic Alphabet\n *\n * @default 'IPA'\n */\n phonetics?: 'LHP' | 'IPA';\n};\n\n/**\n * Extended route representation options.\n *\n * Controls which additional progress information is included at route polyline points.\n *\n * @remarks\n * - `distance`: Cumulative distance from start to each point\n * - `travelTime`: Cumulative travel time from start to each point\n *\n * @example\n * ```typescript\n * // Include both distance and time progress\n * const extended: ExtendedRouteRepresentation[] = ['distance', 'travelTime'];\n * ```\n *\n * @default ['distance', 'travelTime']\n *\n * @group Routing\n */\nexport type ExtendedRouteRepresentation = 'distance' | 'travelTime';\n\n/**\n * Traffic computation mode for alternative travel time estimates.\n *\n * Controls whether to calculate additional travel times using different traffic scenarios.\n *\n * @remarks\n * - `none`: Only return default best-estimate travel time\n * - `all`: Return travel times for multiple traffic scenarios:\n * - No traffic (free flow)\n * - Historic traffic patterns\n * - Live traffic incidents\n *\n * @example\n * ```typescript\n * // Get all traffic-based time estimates\n * const computeTravelTimeFor: ComputeTravelTimeFor = 'all';\n * ```\n *\n * @default 'none'\n *\n * @group Routing\n */\nexport type ComputeTravelTimeFor = 'none' | 'all';\n\n/**\n * Side of the road to arrive on.\n *\n * @group Routing\n */\nexport const arrivalSides = ['any', 'curb'] as const;\n\n/**\n * Which side of the road the route should arrive on at each stop.\n *\n * @remarks\n * - `any`: arrive from either side, whichever is faster\n * - `curb`: arrive on the curb side for the country's driving direction\n *\n * @example\n * ```typescript\n * const arrivalSide: ArrivalSide = 'curb';\n * ```\n *\n * @default 'any'\n *\n * @group Routing\n */\nexport type ArrivalSide = (typeof arrivalSides)[number];\n\n/**\n * Maximum number of alternative routes to calculate.\n *\n * Alternative routes provide different options for traveling between the same origin and destination.\n *\n * @remarks\n * - `0`: Only calculate the best route (default)\n * - `1-5`: Calculate best route plus up to N alternatives\n *\n * More alternatives increase computation time but provide more route options.\n *\n * @example\n * ```typescript\n * // Request up to 2 alternative routes\n * const maxAlternatives: MaxNumberOfAlternatives = 2;\n * ```\n *\n * @default 0\n *\n * @group Routing\n */\nexport type MaxNumberOfAlternatives = 0 | 1 | 2 | 3 | 4 | 5;\n\n/**\n * Parameters for calculating a route between waypoints.\n *\n * Includes origin, destination, optional intermediate waypoints, and various routing options\n * to customize the calculated route based on vehicle type, traffic, and preferences.\n *\n * @example\n * ```typescript\n * // Basic route from A to B\n * const params: CalculateRouteParams = {\n * apiKey: 'your-api-key',\n * locations: [\n * [4.9041, 52.3676], // Amsterdam\n * [4.4777, 51.9244] // Rotterdam\n * ]\n * };\n *\n * // Route with guidance and alternatives\n * const advancedParams: CalculateRouteParams = {\n * apiKey: 'your-api-key',\n * locations: [[4.9041, 52.3676], [4.4777, 51.9244]],\n * guidance: { type: 'coded', phonetics: 'IPA' },\n * maxAlternatives: 2,\n * costModel: {\n * routeType: 'fast',\n * avoid: ['tollRoads', 'motorways']\n * },\n * when: {\n * option: 'departAt',\n * date: new Date('2025-10-20T08:00:00Z')\n * }\n * };\n *\n * // Electric vehicle route with charging\n * const evParams: CalculateRouteParams = {\n * apiKey: 'your-api-key',\n * locations: [[4.9, 52.3], [8.5, 50.1]],\n * vehicle: {\n * engineType: 'electric',\n * model: {\n * engine: {\n * charging: { maxChargeKWH: 85 },\n * consumption: {\n * speedsToConsumptionsKWH: [\n * { speedKMH: 50, consumptionUnitsPer100KM: 8 },\n * { speedKMH: 80, consumptionUnitsPer100KM: 12 },\n * { speedKMH: 120, consumptionUnitsPer100KM: 18 }\n * ]\n * }\n * }\n * },\n * state: { currentChargeInkWh: 50 }\n * }\n * };\n * ```\n *\n * @group Routing\n */\nexport type CalculateRouteParams = CommonServiceParams<CalculateRouteRequestAPI, CalculateRouteResponseAPI> &\n CommonRoutingParams & {\n /**\n * Ordered list of locations (waypoints) and/or path points for route calculation.\n *\n * The route will pass through these locations in the specified order.\n *\n * @remarks\n * Requirements:\n * - Minimum 2 waypoints (origin and destination) OR 1 path with 2+ points\n *\n * Supported formats:\n * - Coordinate arrays: `[longitude, latitude]`\n * - Path arrays for route reconstruction\n * - Waypoint Features, whose properties carry the per-stop options\n *\n * The route ends a leg at every location in this list. There is no circle (soft) waypoint\n * that the route merely passes: the routing API takes a plain point for each one. A path\n * pins the geometry the route must follow, but its endpoints go out as waypoints as well,\n * so it shapes the route without shortening the list of legs.\n *\n * @see [POST data parameters](https://docs.tomtom.com/routing-api/documentation/tomtom-maps/calculate-route#post-data-parameters)\n * @see [Response structure](https://docs.tomtom.com/routing-api/documentation/tomtom-maps/calculate-route#structure-of-a-successful-response)\n *\n * @example\n * ```typescript\n * // Coordinate arrays\n * locations: [[4.9, 52.3], [4.5, 51.9]]\n *\n * // With intermediate stop\n * locations: [[4.9, 52.3], [4.7, 52.1], [4.5, 51.9]]\n *\n * // Path array for route reconstruction\n * locations: [\n * [4.9, 52.3], // Origin waypoint\n * [\n * [4.85, 52.25], [4.80, 52.20], [4.75, 52.15] // Path points between waypoints\n * ],\n * [4.5, 51.9] // Destination waypoint\n * ]\n * ```\n */\n locations: RoutePlanningLocation[];\n\n /**\n * Controls how entry points are used for routing.\n *\n * Entry points represent specific building entrances or facility access points.\n * Using them improves routing accuracy by directing to the correct entrance.\n *\n * @remarks\n * - `main-when-available`: Use main entry point if available, otherwise use place center (recommended for routing)\n * - `ignore`: Always use place center position\n *\n * @default 'main-when-available'\n *\n * @example\n * ```typescript\n * // Route to main entrance when available\n * useEntryPoints: 'main-when-available'\n * ```\n */\n useEntryPoints?: GetPositionEntryPointOption;\n\n /**\n * Which side of the road the route should arrive on.\n *\n * @remarks\n * - `any`: arrive from either side, whichever is faster\n * - `curb`: arrive on the curb side for the country's driving direction, so passengers or\n * deliveries can leave the vehicle without crossing the road\n *\n * Applies to the destination and to every intermediate stop.\n *\n * @default 'any'\n *\n * @example\n * ```typescript\n * // Pull up on the passenger side at each stop\n * arrivalSide: 'curb'\n * ```\n */\n arrivalSide?: ArrivalSide;\n\n /**\n * Request travel time calculations for different traffic scenarios.\n *\n * When set to 'all', the returned route summary will contain extra fields:\n * - `noTrafficTravelTimeInSeconds` – Free-flow (no traffic)\n * - `historicTrafficTravelTimeInSeconds` – Historic traffic patterns\n * - `liveTrafficIncidentsTravelTimeInSeconds` – Current live traffic\n *\n * Useful for comparing traffic impact and displaying \"X minutes saved by leaving now\".\n *\n * @default 'none'\n *\n * @example\n * ```typescript\n * computeTravelTimeFor: 'all'\n * ```\n */\n computeTravelTimeFor?: ComputeTravelTimeFor;\n\n /**\n * Request extended progress information at route polyline points.\n *\n * Includes cumulative distance and/or time from start to each coordinate.\n * When non-empty, the route features will contain additional property \"progress\" with an array of objects containing:\n * - `pointIndex` – Index of the coordinate in the route geometry\n * - `travelTimeInSeconds` - Cumulative travel time from start to this point (if \"travelTime\" is requested)\n * - `distanceInMeters` - Cumulative distance from start to this point (if \"distance\" is requested)\n *\n * Useful for displaying progress during navigation or animating route visualization.\n *\n * @remarks\n * Orbis v3 has no per-representation selector: progress points are all-or-nothing. Any\n * non-empty array therefore requests both `distance` and `travelTime`. Pass an empty array\n * to opt out entirely and keep the progress data out of the response payload.\n *\n * @default ['distance', 'travelTime']\n *\n * @example\n * ```typescript\n * extendedRouteRepresentations: ['distance', 'travelTime']\n *\n * // Opt out of progress points altogether\n * extendedRouteRepresentations: []\n * ```\n */\n extendedRouteRepresentations?: ExtendedRouteRepresentation[];\n\n /**\n * Request turn-by-turn guidance instructions.\n *\n * When specified, the response includes detailed navigation instructions\n * with maneuvers, road names, and optional phonetics.\n *\n * @example\n * ```typescript\n * guidance: {\n * type: 'coded',\n * phonetics: 'IPA'\n * }\n * ```\n */\n guidance?: GuidanceParams;\n\n /**\n * Route section types to include in the response.\n *\n * Sections help you display route characteristics like tolls, ferries, or traffic.\n * Leg sections are always included regardless of this parameter. A section type is only\n * present in the response if the route actually traverses it. The `lanes` section is\n * guidance-only and is returned solely when {@link GuidanceParams | `guidance`} is also\n * requested.\n *\n * @default When omitted, all section types are requested.\n *\n * @example\n * ```typescript\n * // Request specific sections (any combination of available section types)\n * sectionTypes: ['toll', 'ferry', 'traffic', 'country', 'roadShields']\n *\n * // Request no optional sections\n * sectionTypes: []\n * ```\n */\n sectionTypes?: InputSectionTypes;\n\n /**\n * Maximum number of alternative routes to calculate.\n *\n * Alternative routes provide different travel options between the same origin and destination.\n * Each alternative is optimized differently (e.g., avoiding highways, minimizing tolls).\n *\n * @remarks\n * - More alternatives increase response time\n * - Alternatives are returned in order of quality (best first)\n * - Not all requests will return the maximum number of alternatives\n *\n * @default 0\n *\n * @example\n * ```typescript\n * // Request up to 2 alternatives\n * maxAlternatives: 2\n * ```\n */\n maxAlternatives?: MaxNumberOfAlternatives;\n };\n","import type { SectionType } from '@tomtom-org/maps-sdk/core';\nimport { getRoutePlanningLocationType, inputSectionTypesWithGuidance } from '@tomtom-org/maps-sdk/core';\nimport { z } from 'zod';\nimport { commonRoutingRequestSchema } from '../shared/schema/commonRoutingRequestSchema';\nimport {\n featureSchema,\n geometrySchema,\n hasLngLatSchema,\n lineStringCoordsSchema,\n} from '../shared/schema/geometriesSchema';\nimport type { SchemaRefinement } from '../shared/types/validation';\nimport { arrivalSides, type CalculateRouteParams } from './types/calculateRouteParams';\n\nconst waypointLikeSchema = z.union([hasLngLatSchema, geometrySchema]);\nconst pathLikeSchema = z.union([lineStringCoordsSchema, featureSchema]);\n\nconst mandatorySchema = z.object({\n locations: z.array(z.union([waypointLikeSchema, pathLikeSchema])).min(1), // see calculateRouteLocationsRefinement\n});\n\nconst optionalSchema = z.object({\n arrivalSide: z.enum(arrivalSides).optional(),\n computeTravelTimeFor: z.enum(['none', 'all']).optional(),\n maxAlternatives: z.number().min(0).max(5).optional(),\n sectionTypes: z.array(z.enum(inputSectionTypesWithGuidance as [SectionType, ...SectionType[]])).optional(),\n});\n\nconst schema = commonRoutingRequestSchema.extend(mandatorySchema.extend(optionalSchema.shape).shape);\n\nconst locationsRefinement: SchemaRefinement<CalculateRouteParams> = {\n check: (data: CalculateRouteParams): boolean => {\n const types = data.locations.map(getRoutePlanningLocationType);\n const waypointCount = types.filter((type) => type === 'waypoint').length;\n const pathCount = types.filter((type) => type === 'path').length;\n // At least 2 waypoints, OR at least 1 path (reconstruction)\n return waypointCount >= 2 || pathCount >= 1;\n },\n message: 'At least 2 waypoints or 1 path (route reconstruction) is required.',\n};\n\n/**\n * @ignore\n */\nexport const routeRequestValidationConfig = { schema, refinements: [locationsRefinement] };\n","import {\n bboxFromGeoJSON,\n getPositionStrict,\n getRoutePlanningLocationType,\n type HasBBox,\n type HasLngLat,\n type LegSectionProps,\n type PathLike,\n type RoutePlanningLocation,\n type TomTomAPIHeaders,\n type WaypointLike,\n type WaypointProps,\n} from '@tomtom-org/maps-sdk/core';\nimport type { Feature, LineString, MultiPoint, Point } from 'geojson';\nimport { isNil } from 'lodash-es';\nimport type {\n ChargingPreferencesKWH,\n ChargingPreferencesPCT,\n CombustionVehicleState,\n ElectricVehicleParams,\n ElectricVehicleStateKWH,\n ElectricVehicleStatePCT,\n FetchInput,\n VehicleParameters,\n} from '../shared';\nimport { buildCommonServiceRequestHeaders } from '../shared/request/requestBuildingUtils';\nimport type {\n CombustionEngineModel,\n ConsumptionModelEfficiency,\n ElectricEngineModel,\n SpeedToConsumptionRate,\n VehicleEngineType,\n} from '../shared/types/vehicleEngineParams';\nimport type { ExplicitVehicleModel } from '../shared/types/vehicleModel';\nimport type {\n AvoidAreasAPI,\n CalculateRoutePOSTDataAPI,\n LegRequestAPI,\n RouteStopRequestAPI,\n} from './types/apiRequestTypes';\nimport type { CalculateRouteParams, GuidanceParams } from './types/calculateRouteParams';\nimport type { RouteStopOptions } from './types/routeStopOptions';\n\nconst buildAvoidAreas = (avoidAreas: HasBBox[]): AvoidAreasAPI => ({\n rectangles: avoidAreas.map((rectangle) => {\n const bbox = bboxFromGeoJSON(rectangle);\n if (!bbox) throw new Error('Could not derive a bounding box from an avoidAreas rectangle');\n\n const [minLon, minLat, maxLon, maxLat] = bbox;\n return { type: 'Feature' as const, bbox: [minLon, minLat, maxLon, maxLat], geometry: null };\n }),\n});\n\nconst toGuidanceBody = (\n guidance: GuidanceParams,\n): { guidance: 'instructions'; instructionPhonetics: 'ipa' | 'lhp' } => ({\n guidance: 'instructions',\n instructionPhonetics: guidance.phonetics ? (guidance.phonetics.toLowerCase() as 'ipa' | 'lhp') : 'ipa',\n});\n\nconst buildSpeedConsumptionString = (rates: SpeedToConsumptionRate[]): string =>\n rates.map((rate) => `${rate.speedKMH},${rate.consumptionUnitsPer100KM}`).join(':');\n\n// V3 section types that are EXPLICIT — they must be individually listed in the Attributes header\n// to be included in the response, even when the parent `sections` is already requested.\n// `lanes` is also EXPLICIT but is guidance-only and handled separately below.\nconst EXPLICIT_SECTION_TYPES = ['tollVignette', 'roadShields', 'importantRoadStretch'] as const;\n\nconst buildAttributesHeader = (params: CalculateRouteParams): string => {\n const parts = ['summary', 'legs(summary,path)'];\n const sectionTypes = params.sectionTypes;\n // V3 returns all non-EXPLICIT section types when sections is requested.\n // Omit sections only when the caller explicitly passes an empty sectionTypes array.\n const requestSections = !sectionTypes || sectionTypes.length > 0;\n if (requestSections) {\n parts.push('sections');\n // EXPLICIT V3 section sub-attributes must be listed individually in the Attributes header.\n // When sectionTypes is undefined the customer wants everything, so request all EXPLICIT types\n // (preserving V2 behavior). When sectionTypes is a specific list, only add the requested ones.\n const wantAll = !sectionTypes;\n for (const type of EXPLICIT_SECTION_TYPES) {\n if (wantAll || sectionTypes.includes(type)) {\n parts.push(`sections.${type}`);\n }\n }\n // sections.lanes is EXPLICIT and only available with guidance.\n if (params.guidance && (wantAll || sectionTypes.includes('lanes'))) {\n parts.push('sections.lanes');\n }\n }\n // V3 dropped the per-representation `extendedRouteRepresentation` parameter: `progressPoints`\n // is all-or-nothing, so a non-empty `extendedRouteRepresentations` array coarsens to \"give me\n // progress points\". An explicitly empty array is the caller opting out, and saves the payload.\n if (params.extendedRouteRepresentations?.length !== 0) parts.push('progressPoints');\n // When guidance is requested, include the EXPLICIT instruction sub-attributes that back\n // fields the V2 SDK exposed: road-shield icon references, signpost exit icon\n // (→ signpostRoadShieldReferences), and the traffic-light offset.\n if (params.guidance) {\n parts.push(\n 'instructions(*,nextRoadInformation.roadShields.iconReference,previousRoadInformation.roadShields.iconReference,signpost.exitIconReference,distanceToPreviousTrafficLightInMeters)',\n );\n }\n // `roadShieldAtlasReference` is an EXPLICIT top-level attribute (sibling of `routes`), used to\n // build road-shield images; request it whenever guidance instructions are requested.\n const routesAttr = `routes(${parts.join(',')})`;\n return params.guidance ? `roadShieldAtlasReference,${routesAttr}` : routesAttr;\n};\n\n// ─── Regular route (V3 /routes/calculate) vehicle body + URL param helpers ───\n\nconst buildVehicleBody = (vehicle: VehicleParameters): Partial<CalculateRoutePOSTDataAPI> => {\n const result: Partial<CalculateRoutePOSTDataAPI> = {};\n const isElectric = (vehicle as ElectricVehicleParams).engineType === 'electric';\n if (isElectric) result.vehicleEngineType = 'electric';\n\n if (vehicle.model && !('variantId' in vehicle.model)) {\n const model = vehicle.model as ExplicitVehicleModel<VehicleEngineType>;\n if (model.dimensions?.weightKG) result.vehicleWeightInKilograms = model.dimensions.weightKG;\n }\n\n if (vehicle.state?.heading) result.vehicleHeadingInDegrees = vehicle.state.heading;\n\n if (vehicle.restrictions?.maxSpeedKMH) result.vehicleMaxSpeedInKilometersPerHour = vehicle.restrictions.maxSpeedKMH;\n\n if (vehicle.restrictions?.tollTransponder)\n result.vehicleHasElectronicTollCollectionTransponder = vehicle.restrictions.tollTransponder;\n\n return result;\n};\n\nconst setEfficiencyParams = (url: URL, efficiency: ConsumptionModelEfficiency): void => {\n if (!isNil(efficiency.acceleration))\n url.searchParams.set('accelerationEfficiency', String(efficiency.acceleration));\n\n if (!isNil(efficiency.deceleration))\n url.searchParams.set('decelerationEfficiency', String(efficiency.deceleration));\n\n if (!isNil(efficiency.uphill)) url.searchParams.set('uphillEfficiency', String(efficiency.uphill));\n\n if (!isNil(efficiency.downhill)) url.searchParams.set('downhillEfficiency', String(efficiency.downhill));\n};\n\n// Electric consumption + max-charge URL params shared by the regular (/routes/calculate) and LDEVR\n// (/calculateLongDistanceEVRoute) request builders.\nconst appendElectricConsumptionParams = (url: URL, engine: ElectricEngineModel): void => {\n const consumption = engine.consumption;\n if (consumption.speedsToConsumptionsKWH?.length)\n url.searchParams.set(\n 'constantSpeedConsumptionInkWhPerHundredkm',\n buildSpeedConsumptionString(consumption.speedsToConsumptionsKWH),\n );\n\n if (!isNil(consumption.auxiliaryPowerInkW))\n url.searchParams.set('auxiliaryPowerInkW', String(consumption.auxiliaryPowerInkW));\n\n if (!isNil(consumption.consumptionInKWHPerKMAltitudeGain))\n url.searchParams.set(\n 'consumptionInkWhPerkmAltitudeGain',\n String(consumption.consumptionInKWHPerKMAltitudeGain),\n );\n\n if (!isNil(consumption.recuperationInKWHPerKMAltitudeLoss))\n url.searchParams.set(\n 'recuperationInkWhPerkmAltitudeLoss',\n String(consumption.recuperationInKWHPerKMAltitudeLoss),\n );\n\n if (consumption.efficiency) setEfficiencyParams(url, consumption.efficiency);\n\n if (engine.charging?.maxChargeKWH) url.searchParams.set('maxChargeInkWh', String(engine.charging.maxChargeKWH));\n};\n\nconst appendElectricParams = (url: URL, engine: ElectricEngineModel, vehicle: VehicleParameters): void => {\n appendElectricConsumptionParams(url, engine);\n\n const kwhState = vehicle.state as ElectricVehicleStateKWH;\n const pctState = vehicle.state as ElectricVehicleStatePCT;\n if (kwhState?.currentChargeInkWh) {\n url.searchParams.set('currentChargeInkWh', String(kwhState.currentChargeInkWh));\n } else if (pctState?.currentChargePCT && engine.charging?.maxChargeKWH) {\n url.searchParams.set(\n 'currentChargeInkWh',\n String((engine.charging.maxChargeKWH * pctState.currentChargePCT) / 100),\n );\n }\n};\n\nconst appendCombustionParams = (url: URL, engine: CombustionEngineModel, vehicle: VehicleParameters): void => {\n const consumption = engine.consumption;\n if (consumption.speedsToConsumptionsLiters?.length)\n url.searchParams.set(\n 'constantSpeedConsumptionInLitersPerHundredkm',\n buildSpeedConsumptionString(consumption.speedsToConsumptionsLiters),\n );\n\n if (!isNil(consumption.auxiliaryPowerInLitersPerHour))\n url.searchParams.set('auxiliaryPowerInLitersPerHour', String(consumption.auxiliaryPowerInLitersPerHour));\n\n if (!isNil(consumption.fuelEnergyDensityInMJoulesPerLiter))\n url.searchParams.set(\n 'fuelEnergyDensityInMJoulesPerLiter',\n String(consumption.fuelEnergyDensityInMJoulesPerLiter),\n );\n\n if (consumption.efficiency) setEfficiencyParams(url, consumption.efficiency);\n\n const combustionState = vehicle.state as CombustionVehicleState;\n if (combustionState?.currentFuelInLiters)\n url.searchParams.set('currentFuelInLiters', String(combustionState.currentFuelInLiters));\n};\n\nconst appendConsumptionParams = (url: URL, vehicle: VehicleParameters): void => {\n if (!vehicle.model || 'variantId' in vehicle.model) return;\n\n const model = vehicle.model as ExplicitVehicleModel<VehicleEngineType>;\n if (!model.engine) return;\n\n if ((vehicle as ElectricVehicleParams).engineType === 'electric') {\n appendElectricParams(url, model.engine as ElectricEngineModel, vehicle);\n } else {\n appendCombustionParams(url, model.engine as CombustionEngineModel, vehicle);\n }\n};\n\n// ─── LDEVR (/calculateLongDistanceEVRoute) URL param helpers ─────────────────\n\nconst getLDEVRMaxCharge = (vehicle: VehicleParameters): number | undefined => {\n if (!vehicle.model || 'variantId' in vehicle.model) return undefined;\n\n return ((vehicle.model as ExplicitVehicleModel<VehicleEngineType>).engine as ElectricEngineModel | undefined)\n ?.charging?.maxChargeKWH;\n};\n\nconst appendLDEVRStateParams = (url: URL, vehicle: VehicleParameters): void => {\n if (!vehicle.state) return;\n\n const kwhState = vehicle.state as ElectricVehicleStateKWH;\n const pctState = vehicle.state as ElectricVehicleStatePCT;\n if (kwhState.currentChargeInkWh) {\n url.searchParams.set('currentChargeInkWh', String(kwhState.currentChargeInkWh));\n return;\n }\n\n if (!pctState.currentChargePCT) return;\n\n const maxCharge = getLDEVRMaxCharge(vehicle);\n if (maxCharge) url.searchParams.set('currentChargeInkWh', String((maxCharge * pctState.currentChargePCT) / 100));\n};\n\nconst appendLDEVRChargingPrefParams = (url: URL, vehicle: VehicleParameters): void => {\n const chargingPrefs = (vehicle as ElectricVehicleParams).preferences?.chargingPreferences;\n if (!chargingPrefs) return;\n\n const kwhPrefs = chargingPrefs as ChargingPreferencesKWH;\n const pctPrefs = chargingPrefs as ChargingPreferencesPCT;\n if (!isNil(kwhPrefs.minChargeAtDestinationInkWh) || !isNil(kwhPrefs.minChargeAtChargingStopsInkWh)) {\n if (!isNil(kwhPrefs.minChargeAtDestinationInkWh))\n url.searchParams.set('minChargeAtDestinationInkWh', String(kwhPrefs.minChargeAtDestinationInkWh));\n\n if (!isNil(kwhPrefs.minChargeAtChargingStopsInkWh))\n url.searchParams.set('minChargeAtChargingStopsInkWh', String(kwhPrefs.minChargeAtChargingStopsInkWh));\n\n return;\n }\n\n const maxCharge = getLDEVRMaxCharge(vehicle);\n if (!maxCharge) return;\n\n if (!isNil(pctPrefs.minChargeAtDestinationPCT))\n url.searchParams.set(\n 'minChargeAtDestinationInkWh',\n String((maxCharge * pctPrefs.minChargeAtDestinationPCT) / 100),\n );\n\n if (!isNil(pctPrefs.minChargeAtChargingStopsPCT))\n url.searchParams.set(\n 'minChargeAtChargingStopsInkWh',\n String((maxCharge * pctPrefs.minChargeAtChargingStopsPCT) / 100),\n );\n};\n\nconst appendLDEVRVehicleParams = (url: URL, vehicle: VehicleParameters | undefined): void => {\n if (!vehicle) return;\n\n if (!vehicle.model) return;\n\n if ('variantId' in vehicle.model) {\n url.searchParams.set('vehicleModelId', vehicle.model.variantId);\n } else {\n const model = vehicle.model as ExplicitVehicleModel<VehicleEngineType>;\n if (model.engine) appendElectricConsumptionParams(url, model.engine as ElectricEngineModel);\n }\n appendLDEVRStateParams(url, vehicle);\n appendLDEVRChargingPrefParams(url, vehicle);\n};\n\nconst isLDEVRRequest = (params: CalculateRouteParams): boolean =>\n !!(params.vehicle as ElectricVehicleParams | undefined)?.preferences?.chargingPreferences;\n\n// ─── Route reconstruction helpers ────────────────────────────────────────────\n\n// The wait lives on core's `WaypointProps`, since the map reads it too; the rest is routing-only.\ntype StopOptions = WaypointProps & RouteStopOptions;\n\n// The builder's own intermediate, not a wire shape — hence no `API` suffix and no place in\n// apiRequestTypes.ts: the coordinate becomes `routePlanningLocations`, the options become part of\n// `legs[]`, and only `legToNext` is already wire-shaped.\ntype Stop = { coordinate: [number, number]; legToNext?: LegRequestAPI; options?: StopOptions };\n\nconst getPathCoordinates = (pathLike: PathLike): [number, number][] => {\n if (Array.isArray(pathLike)) return pathLike as [number, number][];\n\n return pathLike.geometry.coordinates as [number, number][];\n};\n\nconst getPathLegSections = (pathLike: PathLike): LegSectionProps[] | undefined => {\n if (Array.isArray(pathLike)) return undefined;\n\n return pathLike.properties?.sections?.leg;\n};\n\nconst expandPathToStops = (pathLike: PathLike): Stop[] => {\n const coordinates = getPathCoordinates(pathLike);\n const legSections = getPathLegSections(pathLike);\n if (\n legSections &&\n legSections.length > 1 &&\n legSections.every((section) => section.startPointIndex != null && section.endPointIndex != null)\n ) {\n const stops: Stop[] = legSections.map((section) => {\n const start = section.startPointIndex ?? 0;\n const end = section.endPointIndex ?? 0;\n return {\n coordinate: coordinates[start],\n legToNext: {\n path: {\n type: 'LineString',\n coordinates: coordinates.slice(start, end + 1),\n },\n },\n };\n });\n stops.push({\n coordinate: coordinates[legSections.at(-1)?.endPointIndex ?? coordinates.length - 1],\n });\n return stops;\n }\n return [\n {\n coordinate: coordinates[0],\n legToNext: { path: { type: 'LineString', coordinates } as LineString },\n },\n { coordinate: coordinates.at(-1) ?? coordinates[0] },\n ];\n};\n\n// Per-stop options ride on the `properties` of a waypoint Feature — see RouteStopOptions.\nconst getRouteStopOptions = (location: RoutePlanningLocation): StopOptions | undefined => {\n if (Array.isArray(location) || !('properties' in location)) return undefined;\n\n return (location as Feature<Point, StopOptions>).properties ?? undefined;\n};\n\nconst flattenLocations = (\n locations: RoutePlanningLocation[],\n useEntryPoint?: CalculateRouteParams['useEntryPoints'],\n): Stop[] => {\n const result: Stop[] = [];\n for (const location of locations) {\n if (getRoutePlanningLocationType(location) === 'waypoint') {\n const options = getRouteStopOptions(location);\n // Explicit candidate entry points are chosen between by the routing engine, so the\n // stop itself must stay the place center rather than a client-side entry point.\n const stopEntryPoint = options?.candidateEntryPoints?.length ? 'ignore' : useEntryPoint;\n const [longitude, latitude] = getPositionStrict(location as WaypointLike, {\n useEntryPoint: stopEntryPoint,\n });\n result.push({ coordinate: [longitude, latitude], ...(options && { options }) });\n } else {\n result.push(...expandPathToStops(location as PathLike));\n }\n }\n return result;\n};\n\nconst toEntryPointsMultiPoint = (entryPoints: HasLngLat[]): MultiPoint => ({\n type: 'MultiPoint',\n coordinates: entryPoints.map((entryPoint) => getPositionStrict(entryPoint)),\n});\n\n// Builds the `legs[]` entry for the leg *arriving at* `arrivingStop`, from that stop's options.\nconst toLegRequest = (arrivingStop: Stop, isDestination: boolean): LegRequestAPI => {\n const options = arrivingStop.options;\n if (!options) return {};\n\n if (isDestination && options.pauseDurationSeconds)\n throw new Error(\n 'pauseDurationSeconds is not supported on the destination: the routing API requires the pause on the last leg to be 0.',\n );\n\n const routeStop: RouteStopRequestAPI = {\n ...(options.pauseDurationSeconds && { pauseDurationInSeconds: options.pauseDurationSeconds }),\n ...(options.candidateEntryPoints?.length && {\n entryPoints: toEntryPointsMultiPoint(options.candidateEntryPoints),\n ...(!isNil(options.preferredEntryPointIndex) && {\n preferredEntryPointIndex: options.preferredEntryPointIndex,\n }),\n }),\n };\n\n return {\n ...(options.legCostModel?.routeType && { routeType: options.legCostModel.routeType }),\n // The route-level `avoids` is a string array; per-leg it takes objects.\n ...(options.legCostModel?.avoid?.length && {\n avoids: options.legCostModel.avoid.map((name) => ({ name })),\n }),\n ...(Object.keys(routeStop).length > 0 && { routeStop }),\n };\n};\n\n// `legs[]` carries route-reconstruction paths and per-stop options; both triggers share the one\n// array, so it is emitted when either applies. Options belong to the stop the leg arrives at,\n// which is what keeps them on the right stop when another stop is inserted earlier in the list.\nconst buildLegs = (allStops: Stop[], hasPathLocations: boolean): LegRequestAPI[] | undefined => {\n // One leg per stop that has something before it, so the index is the departing stop's.\n const arrivingStops = allStops.slice(1);\n const legs = arrivingStops.map((arrivingStop, index) => ({\n ...allStops[index].legToNext,\n ...toLegRequest(arrivingStop, index === arrivingStops.length - 1),\n }));\n\n if (!hasPathLocations && legs.every((leg) => Object.keys(leg).length === 0)) return undefined;\n\n return legs;\n};\n\nconst buildRoutePlanningLocations = (allStops: Stop[]) => {\n const origin: Point = { type: 'Point', coordinates: allStops[0].coordinate };\n const destination: Point = {\n type: 'Point',\n coordinates: (allStops.at(-1) ?? allStops[0]).coordinate,\n };\n const middleStops = allStops.slice(1, -1);\n return {\n origin,\n destination,\n ...(middleStops.length && {\n waypoints: {\n type: 'MultiPoint' as const,\n coordinates: middleStops.map((stop) => stop.coordinate),\n },\n }),\n };\n};\n\n// ─── Main exported function helpers ──────────────────────────────────────────\n\nconst buildCommonBodyFields = (params: CalculateRouteParams) => ({\n ...(params.costModel?.routeType && { routeType: params.costModel.routeType }),\n ...(params.costModel?.traffic && { traffic: params.costModel.traffic }),\n ...(params.costModel?.avoid?.length && { avoids: params.costModel.avoid as string[] }),\n ...(params.travelMode && { travelMode: params.travelMode }),\n ...(params.maxAlternatives && { maxPathAlternativeRoutes: params.maxAlternatives }),\n ...(params.when?.option === 'departAt' &&\n params.when.date && { departureDateTime: params.when.date.toISOString() }),\n ...(params.when?.option === 'arriveBy' && params.when.date && { arrivalDateTime: params.when.date.toISOString() }),\n ...(params.guidance && toGuidanceBody(params.guidance)),\n ...(params.costModel?.avoidAreas?.length && { avoidAreas: buildAvoidAreas(params.costModel.avoidAreas) }),\n ...(params.arrivalSide && {\n arrivalSidePreference: params.arrivalSide === 'curb' ? ('curbSide' as const) : ('anySide' as const),\n }),\n});\n\nconst appendTravelTimeParam = (url: URL, params: CalculateRouteParams): void => {\n if (params.computeTravelTimeFor) url.searchParams.set('computeTravelTimeFor', params.computeTravelTimeFor);\n};\n\nconst buildRequestHeaders = (params: CalculateRouteParams): TomTomAPIHeaders => ({\n ...buildCommonServiceRequestHeaders(params),\n Attributes: buildAttributesHeader(params),\n});\n\nconst getLDEVRChargingModel = (params: CalculateRouteParams) =>\n params.vehicle?.model && !('variantId' in params.vehicle.model)\n ? ((params.vehicle.model as ExplicitVehicleModel<VehicleEngineType>).engine as ElectricEngineModel | undefined)\n ?.charging\n : undefined;\n\nconst buildLDEVRRequest = (\n params: CalculateRouteParams,\n sharedBody: CalculateRoutePOSTDataAPI,\n headers: TomTomAPIHeaders,\n): FetchInput<CalculateRoutePOSTDataAPI> => {\n const baseURL =\n params.customServiceBaseURL ?? `${params.commonBaseURL}/maps/orbis/routing/calculateLongDistanceEVRoute`;\n const url = new URL(baseURL);\n appendLDEVRVehicleParams(url, params.vehicle);\n\n const chargingModel = getLDEVRChargingModel(params);\n const hasPredefinedModel = !!params.vehicle?.model && 'variantId' in params.vehicle.model;\n // `chargingParameters` is mandatory on this endpoint, but the endpoint itself is selected by\n // `vehicle.preferences.chargingPreferences` — a different part of the params. Without either a\n // predefined model (the API supplies the charging model for it) or explicit connectors, the\n // request is guaranteed to fail, so fail here with a message that names the actual cause.\n if (!hasPredefinedModel && !chargingModel?.chargingConnectors?.length)\n throw new Error(\n 'Charging preferences require a charging model: set vehicle.model.engine.charging.chargingConnectors (or a predefined vehicle.model.variantId) to plan charging stops.',\n );\n\n // Only ever *adds* to the shared body: every field both endpoints take is built once, before\n // the branch, so a field added there cannot reach one endpoint and silently miss the other.\n const body: CalculateRoutePOSTDataAPI = {\n ...sharedBody,\n ...(chargingModel && {\n chargingParameters: {\n ...(chargingModel.batteryCurve?.length && { batteryCurve: chargingModel.batteryCurve }),\n ...(chargingModel.chargingConnectors?.length && {\n chargingConnectors: [...chargingModel.chargingConnectors],\n }),\n ...(!isNil(chargingModel.chargingTimeOffsetInSec) && {\n chargingTimeOffsetInSec: chargingModel.chargingTimeOffsetInSec,\n }),\n },\n }),\n // This endpoint is electric by definition, whatever the vehicle body derived.\n vehicleEngineType: 'electric',\n };\n\n return { method: 'POST', url, data: body, headers };\n};\n\n// ─── Main exported function ───────────────────────────────────────────────────\n\n/**\n * Default function for building calculate route request from {@link CalculateRouteParams}\n * targeting the OrbisV3 routing API.\n * @param params The calculate route parameters, with global configuration already merged into them.\n */\nexport const buildCalculateRouteRequest = (params: CalculateRouteParams): FetchInput<CalculateRoutePOSTDataAPI> => {\n const hasPathLocations = params.locations.map(getRoutePlanningLocationType).includes('path');\n const allStops = flattenLocations(params.locations, params.useEntryPoints);\n const routePlanningLocations = buildRoutePlanningLocations(allStops);\n const commonBodyFields = buildCommonBodyFields(params);\n const headers = buildRequestHeaders(params);\n // Everything above, plus `legs` and the travel-time param below, is shared by both endpoints:\n // the same CalculateRouteParams must mean the same thing whether or not charging preferences\n // routed it to LDEVR.\n const legs = buildLegs(allStops, hasPathLocations);\n const sharedBody: CalculateRoutePOSTDataAPI = {\n routePlanningLocations,\n ...commonBodyFields,\n ...(legs && { legs }),\n ...(params.vehicle && buildVehicleBody(params.vehicle)),\n };\n if (isLDEVRRequest(params)) {\n const ldevrRequest = buildLDEVRRequest(params, sharedBody, headers);\n appendTravelTimeParam(ldevrRequest.url, params);\n return ldevrRequest;\n }\n\n // `vehicleModelId` is only supported by the LDEVR endpoint, which is reached by setting\n // `vehicle.preferences.chargingPreferences`. Sending it here returns\n // `400 parameter [vehicleModelId] not supported`, and dropping it silently would plan the\n // route for a default vehicle instead of the requested one.\n if (params.vehicle?.model && 'variantId' in params.vehicle.model)\n throw new Error(\n 'vehicle.model.variantId is only supported for EV routes with charging stops: set vehicle.preferences.chargingPreferences, or describe the vehicle with vehicle.model.dimensions and vehicle.model.engine instead.',\n );\n\n const baseURL = params.customServiceBaseURL ?? `${params.commonBaseURL}/maps/orbis/routing/routes/calculate`;\n const url = new URL(baseURL);\n if (params.vehicle) appendConsumptionParams(url, params.vehicle);\n\n appendTravelTimeParam(url, params);\n\n return { method: 'POST', url, data: sharedBody, headers };\n};\n","import {\n BBox,\n bboxFromGeoJSON,\n type ChargingStop,\n type CountrySectionProps,\n type CurrentType,\n type DelayMagnitude,\n type DrivingSide,\n type Guidance,\n generateId,\n type ImportantRoadStretchProps,\n type Instruction,\n type Landmark,\n type LaneDirection,\n type LaneSectionProps,\n type LegSectionProps,\n type LegSummary,\n type Maneuver,\n type PossibleLaneDirection,\n type PossibleLaneSeparator,\n type RoadInformation,\n type RoadInformationProperty,\n type RoadShield,\n type RoadShieldReference,\n type RoadShieldSectionProps,\n type Route,\n type RouteSummary,\n type Routes,\n type SectionProps,\n type SectionsProps,\n type Signpost,\n type SpeedLimitSectionProps,\n type TextWithPhonetics,\n type TollPaymentType,\n type TrafficIncidentCategory,\n type TrafficIncidentTEC,\n type TrafficSectionProps,\n} from '@tomtom-org/maps-sdk/core';\nimport type { LineString, Position } from 'geojson';\nimport { isNil, omit } from 'lodash-es';\nimport { toChargingSpeed } from '../shared/ev';\nimport { toIso3 } from '../shared/iso2ToIso3';\nimport { ExplicitVehicleModel } from '../shared/types/vehicleModel';\nimport type {\n BasicSectionAPI,\n CalculateRouteResponseAPI,\n ChargingStopAPI,\n CountrySectionAPI,\n CurrentTypeAPI,\n ImportantRoadStretchSectionAPI,\n InstructionAPI,\n InstructionRoadInformationAPI,\n InstructionRoadShieldAPI,\n LanesSectionAPI,\n LegAPI,\n ProgressPointAPI,\n RouteAPI,\n SpeedLimitSectionAPI,\n SummaryAPI,\n TextWithPhoneticsAPI,\n TrafficSectionAPI,\n TravelModeSectionAPI,\n} from './types/apiResponseTypes';\nimport type { CalculateRouteParams } from './types/calculateRouteParams';\n\nconst toCurrentType = (apiCurrentType: CurrentTypeAPI): CurrentType | undefined => {\n switch (apiCurrentType) {\n case 'Direct_Current':\n return 'DC';\n case 'Alternating_Current_1_Phase':\n return 'AC1';\n case 'Alternating_Current_3_Phase':\n return 'AC3';\n default:\n return undefined;\n }\n};\n\nconst toChargingStop = (\n chargingInformationAtEndOfLeg: ChargingStopAPI,\n maxChargeKWH: number | undefined,\n): ChargingStop => {\n const chargingConnectionInfo = chargingInformationAtEndOfLeg.chargingConnectionInfo;\n const chargingParkLocation = chargingInformationAtEndOfLeg.chargingParkLocation;\n const coordinates = [chargingParkLocation.coordinate.longitude, chargingParkLocation.coordinate.latitude];\n\n const addressParts = [chargingParkLocation.street, chargingParkLocation.houseNumber].filter(Boolean);\n const freeformAddress = addressParts.length > 0 ? addressParts.join(', ') : '';\n\n return {\n type: 'Feature',\n id: chargingInformationAtEndOfLeg.chargingParkId,\n geometry: { type: 'Point', coordinates },\n properties: {\n ...omit(chargingInformationAtEndOfLeg, ['chargingConnectionInfo', 'chargingParkLocation']),\n type: 'POI',\n address: {\n freeformAddress,\n ...(chargingParkLocation.street && { streetName: chargingParkLocation.street }),\n ...(chargingParkLocation.houseNumber && { streetNumber: chargingParkLocation.houseNumber }),\n ...(chargingParkLocation.city && { municipality: chargingParkLocation.city }),\n ...(chargingParkLocation.region && { countrySubdivision: chargingParkLocation.region }),\n ...(chargingParkLocation.postalCode && { postalCode: chargingParkLocation.postalCode }),\n ...(chargingParkLocation.country && { country: chargingParkLocation.country }),\n },\n ...(chargingConnectionInfo && {\n chargingConnectionInfo: {\n plugType: chargingConnectionInfo.chargingPlugType,\n currentInA: chargingConnectionInfo.chargingCurrentInA,\n voltageInV: chargingConnectionInfo.chargingVoltageInV,\n chargingPowerInkW: chargingConnectionInfo.chargingPowerInkW,\n currentType: toCurrentType(chargingConnectionInfo.chargingCurrentType),\n chargingSpeed: toChargingSpeed(chargingConnectionInfo.chargingPowerInkW),\n },\n }),\n ...(maxChargeKWH && {\n targetChargeInPCT: (100 * chargingInformationAtEndOfLeg.targetChargeInkWh) / maxChargeKWH,\n }),\n ...(chargingInformationAtEndOfLeg.chargingParkPowerInkW && {\n chargingParkSpeed: toChargingSpeed(chargingInformationAtEndOfLeg.chargingParkPowerInkW),\n }),\n },\n };\n};\n\nconst parseSummary = (apiSummary: SummaryAPI, params: CalculateRouteParams): RouteSummary | LegSummary => {\n const maxChargeKWH = (params?.vehicle?.model as ExplicitVehicleModel<'electric'>)?.engine?.charging?.maxChargeKWH;\n return {\n lengthInMeters: apiSummary.lengthInMeters,\n travelTimeInSeconds: apiSummary.travelDurationInSeconds,\n trafficDelayInSeconds: apiSummary.trafficDelayDurationInSeconds ?? 0,\n trafficLengthInMeters: apiSummary.trafficLengthInMeters ?? 0,\n historicTrafficTravelTimeInSeconds: apiSummary.historicTrafficTravelTimeInSeconds,\n liveTrafficIncidentsTravelTimeInSeconds: apiSummary.liveTrafficIncidentsTravelTimeInSeconds,\n noTrafficTravelTimeInSeconds: apiSummary.noTrafficTravelTimeInSeconds,\n departureTime: new Date(apiSummary.departureDateTime),\n arrivalTime: new Date(apiSummary.arrivalDateTime),\n deviationDistanceInMeters: apiSummary.deviationDistanceInMeters,\n deviationTimeInSeconds: apiSummary.deviationDurationInSeconds,\n ...(apiSummary.deviationPoint && {\n deviationPoint: apiSummary.deviationPoint.coordinates,\n }),\n fuelConsumptionInLiters: apiSummary.fuelConsumptionInLiters,\n totalChargingTimeInSeconds: apiSummary.totalChargingTimeInSeconds,\n batteryConsumptionInkWh: apiSummary.batteryConsumptionInKilowattHours ?? apiSummary.batteryConsumptionInkWh,\n ...(maxChargeKWH &&\n (() => {\n const batteryConsumption =\n apiSummary.batteryConsumptionInKilowattHours ?? apiSummary.batteryConsumptionInkWh;\n return batteryConsumption\n ? { batteryConsumptionInPCT: (100 * batteryConsumption) / maxChargeKWH }\n : undefined;\n })()),\n remainingChargeAtArrivalInkWh: apiSummary.remainingChargeAtArrivalInkWh,\n ...(maxChargeKWH &&\n apiSummary.remainingChargeAtArrivalInkWh && {\n remainingChargeAtArrivalInPCT: (100 * apiSummary.remainingChargeAtArrivalInkWh) / maxChargeKWH,\n }),\n ...(apiSummary.chargingInformationAtEndOfLeg && {\n chargingInformationAtEndOfLeg: toChargingStop(apiSummary.chargingInformationAtEndOfLeg, maxChargeKWH),\n }),\n };\n};\n\nconst parseRoutePath = (apiRouteLegs: LegAPI[]): LineString => ({\n type: 'LineString',\n coordinates: apiRouteLegs.flatMap((leg) => leg.path.coordinates),\n});\n\n// A stop is only visible as the gap between arriving on one leg and departing on the next, so it\n// takes the pair to see it. Everything spent there widens that one gap -- a requested wait, or\n// charging on an EV route, or both at one stop -- so the gap is reported as it is, and\n// `chargingInformationAtEndOfLeg` is left to say how much of it was charging.\nconst parseLegStopTime = (leg: LegAPI, nextLeg: LegAPI | undefined): number | undefined => {\n if (!nextLeg) return undefined;\n\n const arrival = new Date(leg.summary.arrivalDateTime).getTime();\n const nextDeparture = new Date(nextLeg.summary.departureDateTime).getTime();\n if (Number.isNaN(arrival) || Number.isNaN(nextDeparture)) return undefined;\n\n const stopSeconds = Math.round((nextDeparture - arrival) / 1000);\n return stopSeconds > 0 ? stopSeconds : undefined;\n};\n\nconst parseLegSectionProps = (apiLegs: LegAPI[], params: CalculateRouteParams): LegSectionProps[] =>\n apiLegs.reduce<LegSectionProps[]>((accumulator, leg, legIndex) => {\n const legCoordinateCount = leg.path.coordinates.length;\n const lastEndIndex = legIndex === 0 ? 0 : accumulator[legIndex - 1]?.endPointIndex;\n let endPointIndex: number | undefined;\n if (!isNil(lastEndIndex)) {\n endPointIndex =\n lastEndIndex === 0 ? Math.max(legCoordinateCount - 1, 0) : lastEndIndex + legCoordinateCount;\n }\n const stopTimeInSeconds = parseLegStopTime(leg, apiLegs[legIndex + 1]);\n accumulator.push({\n ...(!isNil(lastEndIndex) && { startPointIndex: lastEndIndex }),\n ...(endPointIndex !== undefined && { endPointIndex }),\n summary: {\n ...(parseSummary(leg.summary, params) as LegSummary),\n ...(stopTimeInSeconds !== undefined && { stopTimeInSeconds }),\n },\n id: generateId(),\n });\n return accumulator;\n }, []);\n\nconst toSectionProps = (section: BasicSectionAPI): SectionProps => ({\n id: generateId(),\n startPointIndex: section.startPathIndex,\n endPointIndex: section.endPathIndex,\n});\n\n/**\n * Map an OrbisV3 traffic section to the SDK's semantic traffic incident categories.\n * Uses TEC main cause codes when available, falling back to the API's coarser `iconCategory`.\n * @ignore\n */\nexport const toTrafficCategories = (section: TrafficSectionAPI): TrafficIncidentCategory[] => {\n if (section.tec?.causes?.length) {\n return section.tec.causes.map((cause) => {\n switch (cause.mainCauseCode) {\n case 1:\n return 'jam';\n case 2:\n return 'accident';\n case 3:\n return 'roadworks';\n case 4:\n return 'narrow-lanes';\n case 5:\n return 'road-closed';\n case 9:\n return 'danger';\n case 11:\n return 'animals-on-road';\n case 13:\n return 'broken-down-vehicle';\n case 16:\n return 'lane-closed';\n case 17:\n return 'wind';\n case 18:\n return 'fog';\n case 19:\n return 'rain';\n case 22:\n return 'frost';\n case 23:\n return 'flooding';\n default:\n return 'other';\n }\n });\n }\n switch (section.iconCategory) {\n case 'jam':\n return ['jam'];\n case 'accident':\n return ['accident'];\n case 'brokenDownVehicle':\n return ['broken-down-vehicle'];\n case 'dangerousConditions':\n return ['danger'];\n case 'flooding':\n return ['flooding'];\n case 'fog':\n return ['fog'];\n case 'ice':\n return ['frost'];\n case 'laneClosed':\n return ['lane-closed'];\n case 'rain':\n return ['rain'];\n case 'roadClosed':\n return ['road-closed'];\n case 'roadWorks':\n return ['roadworks'];\n case 'wind':\n return ['wind'];\n default:\n return ['other'];\n }\n};\n\ntype BasicSectionKey =\n | 'carTrain'\n | 'ferry'\n | 'tunnel'\n | 'motorway'\n | 'pedestrian'\n | 'toll'\n | 'carpool'\n | 'urban'\n | 'unpaved'\n | 'lowEmissionZone';\n\nconst parseSpeedLimitSections = (speedLimitSections: SpeedLimitSectionAPI[]): SpeedLimitSectionProps[] =>\n // V3 carries speeds under `speedRestrictions`; the SDK exposes only the maximum limit.\n speedLimitSections\n .map((speedLimit): SpeedLimitSectionProps | undefined => {\n const max = speedLimit.speedRestrictions?.find((r) => r.type === 'maximum')?.inKilometersPerHour;\n return isNil(max) ? undefined : { ...toSectionProps(speedLimit), maxSpeedLimitInKmh: max };\n })\n .filter((speedLimit): speedLimit is SpeedLimitSectionProps => !!speedLimit);\n\nconst parseLaneSections = (laneSections: LanesSectionAPI[]): LaneSectionProps[] =>\n // V3 lane directions/separators are camelCase strings; map to the SDK's UPPER_SNAKE enums.\n laneSections.map(\n (lanesSection): LaneSectionProps => ({\n ...toSectionProps(lanesSection),\n lanes: (lanesSection.lanes ?? []).map(\n (lane): LaneDirection => ({\n directions: lane.directions.map(\n (direction) => V3_LANE_DIRECTION_MAP[direction] ?? (direction as PossibleLaneDirection),\n ),\n ...(lane.follow && {\n follow: V3_LANE_DIRECTION_MAP[lane.follow] ?? (lane.follow as PossibleLaneDirection),\n }),\n }),\n ),\n laneSeparators: (lanesSection.laneSeparators ?? []).map(\n (separator) => V3_LANE_SEPARATOR_MAP[separator] ?? (separator as PossibleLaneSeparator),\n ),\n properties: lanesSection.properties,\n }),\n );\n\nconst parseImportantRoadStretchSections = (\n importantRoadStretchSections: ImportantRoadStretchSectionAPI[],\n): ImportantRoadStretchProps[] =>\n importantRoadStretchSections.map(\n (importantRoadStretch): ImportantRoadStretchProps => ({\n ...toSectionProps(importantRoadStretch),\n index: importantRoadStretch.importantRoadStretchIndex,\n ...(importantRoadStretch.streetName && { streetName: importantRoadStretch.streetName.text }),\n ...(importantRoadStretch.roadNumbers?.length && {\n roadNumbers: importantRoadStretch.roadNumbers.map((roadNumber) => roadNumber.text),\n }),\n }),\n );\n\n// V3 always returns every section type when sections are requested. Restore the legacy filtering by\n// dropping the section keys the caller did not ask for (the always-present `leg` is kept).\nconst applyRequestedSectionFilter = (\n result: SectionsProps,\n sectionTypes: CalculateRouteParams['sectionTypes'],\n): void => {\n if (!sectionTypes) return;\n const requested = new Set<string>(sectionTypes);\n for (const key of Object.keys(result) as (keyof SectionsProps)[]) {\n if (key !== 'leg' && !requested.has(key)) {\n delete result[key];\n }\n }\n};\n\nconst parseSections = (apiRoute: RouteAPI, params: CalculateRouteParams): SectionsProps => {\n const result: SectionsProps = {\n leg: parseLegSectionProps(apiRoute.legs, params),\n };\n\n const sections = apiRoute.sections;\n if (!sections) return result;\n\n const addBasic = (key: BasicSectionKey, items?: BasicSectionAPI[]) => {\n if (items?.length) result[key] = items.map(toSectionProps);\n };\n\n addBasic('carTrain', sections.carTrain);\n addBasic('ferry', sections.ferry);\n addBasic('tunnel', sections.tunnel);\n addBasic('motorway', sections.motorway);\n addBasic('pedestrian', sections.pedestrian);\n addBasic('toll', sections.toll);\n addBasic('carpool', sections.carpool);\n addBasic('urban', sections.urban);\n addBasic('unpaved', sections.unpaved);\n addBasic('lowEmissionZone', sections.lowEmissionZone);\n\n if (sections.country?.length) {\n result.country = sections.country.map((country: CountrySectionAPI) => ({\n ...toSectionProps(country),\n countryCodeISO3: toIso3(country.countryCodeIso2 ?? ''),\n }));\n }\n\n if (sections.travelMode?.length) {\n result.vehicleRestricted = sections.travelMode\n .filter((travelModeSection: TravelModeSectionAPI) => travelModeSection.travelMode === 'other')\n .map(toSectionProps);\n }\n\n if (sections.traffic?.length) {\n result.traffic = sections.traffic.map(\n (traffic: TrafficSectionAPI): TrafficSectionProps => ({\n ...toSectionProps(traffic),\n delayInSeconds: traffic.delayDurationInSeconds,\n effectiveSpeedInKmh: traffic.effectiveSpeedInKilometersPerHour,\n categories: toTrafficCategories(traffic),\n magnitudeOfDelay: (traffic.delayMagnitude === 'undefined'\n ? 'indefinite'\n : traffic.delayMagnitude) as DelayMagnitude,\n tec: traffic.tec as TrafficIncidentTEC,\n }),\n );\n }\n\n if (sections.speedLimit?.length) {\n result.speedLimit = parseSpeedLimitSections(sections.speedLimit);\n }\n\n if (sections.lanes?.length) {\n result.lanes = parseLaneSections(sections.lanes);\n }\n\n if (sections.roadShields?.length) {\n result.roadShields = sections.roadShields.map(\n (roadShield): RoadShieldSectionProps => ({\n ...toSectionProps(roadShield),\n roadShieldReferences: roadShield.roadShieldReferences as RoadShieldReference[],\n }),\n );\n }\n\n if (sections.tollVignette?.length) {\n result.tollVignette = sections.tollVignette.map(\n (tollVignette: CountrySectionAPI): CountrySectionProps => ({\n ...toSectionProps(tollVignette),\n countryCodeISO3: toIso3(tollVignette.countryCodeIso2 ?? ''),\n }),\n );\n }\n\n if (sections.importantRoadStretch?.length) {\n result.importantRoadStretch = parseImportantRoadStretchSections(sections.importantRoadStretch);\n }\n\n applyRequestedSectionFilter(result, params.sectionTypes);\n\n return result;\n};\n\nconst DELTA = 0.0001;\nconst similar = (pointA: Position, pointB: Position): boolean =>\n Math.abs(pointA[0] - pointB[0]) < DELTA && Math.abs(pointA[1] - pointB[1]) < DELTA;\n\n// V3 maneuver camelCase JSON values → SDK UPPER_SNAKE_CASE Maneuver type.\n// The two renamed cases: continueStraight → STRAIGHT, passTollgate → TOLLGATE.\nconst V3_MANEUVER_MAP: Record<string, Maneuver> = {\n depart: 'DEPART',\n waypointLeft: 'WAYPOINT_LEFT',\n waypointRight: 'WAYPOINT_RIGHT',\n waypointReached: 'WAYPOINT_REACHED',\n waypointAhead: 'WAYPOINT_AHEAD',\n arrive: 'ARRIVE',\n arriveLeft: 'ARRIVE_LEFT',\n arriveRight: 'ARRIVE_RIGHT',\n arriveAhead: 'ARRIVE_AHEAD',\n continueStraight: 'STRAIGHT',\n keepLeft: 'KEEP_LEFT',\n keepRight: 'KEEP_RIGHT',\n turnSlightRight: 'SLIGHT_RIGHT',\n turnRight: 'TURN_RIGHT',\n turnSharpRight: 'SHARP_RIGHT',\n turnSlightLeft: 'SLIGHT_LEFT',\n turnLeft: 'TURN_LEFT',\n turnSharpLeft: 'SHARP_LEFT',\n makeUTurn: 'MAKE_UTURN',\n roundaboutStraight: 'ROUNDABOUT_STRAIGHT',\n roundaboutSharpRight: 'ROUNDABOUT_SHARP_RIGHT',\n roundaboutRight: 'ROUNDABOUT_RIGHT',\n roundaboutSlightRight: 'ROUNDABOUT_SLIGHT_RIGHT',\n roundaboutSlightLeft: 'ROUNDABOUT_SLIGHT_LEFT',\n roundaboutLeft: 'ROUNDABOUT_LEFT',\n roundaboutSharpLeft: 'ROUNDABOUT_SHARP_LEFT',\n roundaboutBack: 'ROUNDABOUT_BACK',\n exitRoundabout: 'EXIT_ROUNDABOUT',\n exitMotorwayLeft: 'EXIT_MOTORWAY_LEFT',\n exitMotorwayRight: 'EXIT_MOTORWAY_RIGHT',\n switchMotorwayLeft: 'SWITCH_MOTORWAY_LEFT',\n switchMotorwayRight: 'SWITCH_MOTORWAY_RIGHT',\n enterCarpoolLaneLeft: 'ENTER_CARPOOL_LANE_LEFT',\n enterCarpoolLaneRight: 'ENTER_CARPOOL_LANE_RIGHT',\n exitCarpoolLaneLeft: 'EXIT_CARPOOL_LANE_LEFT',\n exitCarpoolLaneRight: 'EXIT_CARPOOL_LANE_RIGHT',\n mergeLeftLane: 'MERGE_LEFT_LANE',\n mergeRightLane: 'MERGE_RIGHT_LANE',\n takeShipFerry: 'TAKE_SHIP_FERRY',\n takeCarTrain: 'TAKE_CAR_TRAIN',\n leaveShipFerry: 'LEAVE_SHIP_FERRY',\n leaveCarTrain: 'LEAVE_CAR_TRAIN',\n crossBorder: 'CROSS_BORDER',\n passTollgate: 'TOLLGATE',\n};\n\nconst V3_ROAD_PROPERTY_MAP: Record<string, RoadInformationProperty> = {\n urban: 'URBAN',\n motorway: 'MOTORWAY',\n controlledAccess: 'CONTROLLED_ACCESS',\n};\n\n// V3 landmark camelCase JSON values → SDK UPPER_SNAKE_CASE Landmark type.\nconst V3_LANDMARK_MAP: Record<string, Landmark> = {\n endOfRoad: 'END_OF_ROAD',\n atTrafficLight: 'AT_TRAFFIC_LIGHT',\n onToBridge: 'ON_TO_BRIDGE',\n onBridge: 'ON_BRIDGE',\n afterBridge: 'AFTER_BRIDGE',\n intoTunnel: 'INTO_TUNNEL',\n insideTunnel: 'INSIDE_TUNNEL',\n afterTunnel: 'AFTER_TUNNEL',\n};\n\n// V3 toll payment camelCase JSON values → SDK UPPER_SNAKE_CASE TollPaymentType.\nconst V3_TOLL_PAYMENT_MAP: Record<string, TollPaymentType> = {\n cashCoinsAndBills: 'CASH_COINS_AND_BILLS',\n cashBillsOnly: 'CASH_BILLS_ONLY',\n cashCoinsOnly: 'CASH_COINS_ONLY',\n cashExactChange: 'CASH_EXACT_CHANGE',\n creditCard: 'CREDIT_CARD',\n debitCard: 'DEBIT_CARD',\n travelCard: 'TRAVEL_CARD',\n etc: 'ETC',\n etcTransponder: 'ETC_TRANSPONDER',\n etcVideoCamera: 'ETC_VIDEO_CAMERA',\n subscription: 'SUBSCRIPTION',\n};\n\n// V3 lane direction camelCase JSON values → SDK UPPER_SNAKE_CASE PossibleLaneDirection.\nconst V3_LANE_DIRECTION_MAP: Record<string, PossibleLaneDirection> = {\n straight: 'STRAIGHT',\n slightRight: 'SLIGHT_RIGHT',\n right: 'RIGHT',\n sharpRight: 'SHARP_RIGHT',\n rightUTurn: 'RIGHT_U_TURN',\n slightLeft: 'SLIGHT_LEFT',\n left: 'LEFT',\n sharpLeft: 'SHARP_LEFT',\n leftUTurn: 'LEFT_U_TURN',\n};\n\n// V3 lane separator camelCase JSON values → SDK UPPER_SNAKE_CASE PossibleLaneSeparator.\nconst V3_LANE_SEPARATOR_MAP: Record<string, PossibleLaneSeparator> = {\n unknown: 'UNKNOWN',\n noMarking: 'NO_MARKING',\n longDashed: 'LONG_DASHED',\n doubleSolid: 'DOUBLE_SOLID',\n singleSolid: 'SINGLE_SOLID',\n solidDashed: 'SOLID_DASHED',\n dashedSolid: 'DASHED_SOLID',\n shortDashed: 'SHORT_DASHED',\n shadedAreaMarking: 'SHADED_AREA_MARKING',\n dashedBlocks: 'DASHED_BLOCKS',\n doubleDashed: 'DOUBLE_DASHED',\n crossingAlert: 'CROSSING_ALERT',\n physicalDivider: 'PHYSICAL_DIVIDER',\n physicalDividerLessThan3m: 'PHYSICAL_DIVIDER_LESS_THAN_3M',\n physicalDividerGuardrail: 'PHYSICAL_DIVIDER_GUARDRAIL',\n curb: 'CURB',\n};\n\nconst EMPTY_ROAD_INFO: RoadInformation = { properties: [] };\n\n// V3 phonetic is an object keyed by alphabet ({lhp}|{ipa}); the SDK exposes a single flat string.\nconst toTextWithPhonetics = (t: TextWithPhoneticsAPI): TextWithPhonetics => {\n const phonetic = t.phonetic?.lhp ?? t.phonetic?.ipa;\n return {\n text: t.text,\n ...(phonetic && { phonetic }),\n ...(t.phoneticLanguageCode && { phoneticLanguageCode: t.phoneticLanguageCode }),\n };\n};\n\nconst parseInstructionRoadInformation = (road: InstructionRoadInformationAPI): RoadInformation => ({\n properties: (road.properties ?? []).map(\n (p: string) => V3_ROAD_PROPERTY_MAP[p] ?? (p.toUpperCase() as RoadInformationProperty),\n ),\n ...(road.roadNames?.length && { streetName: toTextWithPhonetics(road.roadNames[0].identifier) }),\n ...(road.roadShields?.length && {\n roadShields: road.roadShields.map(\n (s: InstructionRoadShieldAPI): RoadShield => ({\n roadNumber: s.roadNumber ? toTextWithPhonetics(s.roadNumber) : { text: '' },\n countryCode: toIso3(s.countryCodeIso2 ?? ''),\n ...(s.countrySubdivisionCodeIso && { stateCode: s.countrySubdivisionCodeIso }),\n ...(s.iconReference && { roadShieldReference: s.iconReference }),\n }),\n ),\n }),\n});\n\nconst parseSignpost = (signpost: NonNullable<InstructionAPI['signpost']>): Signpost | undefined => {\n if (!signpost.exitName && !signpost.exitNumber && !signpost.towardName) return undefined;\n return {\n ...(signpost.exitName && { exitName: toTextWithPhonetics(signpost.exitName) }),\n ...(signpost.exitNumber && { exitNumber: toTextWithPhonetics(signpost.exitNumber) }),\n // towardName is required on the SDK type; default to empty text when the API omits it.\n towardName: signpost.towardName ? toTextWithPhonetics(signpost.towardName) : { text: '' },\n };\n};\n\n// Tollgate + border-crossing fields, only present on the relevant maneuvers (kept separate to keep\n// the main instruction builder's complexity in check).\nconst parseTollAndCrossing = (a: InstructionAPI): Partial<Instruction> => ({\n ...(a.tollgateName && { tollgateName: toTextWithPhonetics(a.tollgateName) }),\n ...(a.tollPaymentTypes?.length && {\n tollPaymentTypes: a.tollPaymentTypes.map((t) => V3_TOLL_PAYMENT_MAP[t] ?? (t as TollPaymentType)),\n }),\n ...(a.countryCrossingFromName && { countryCrossingFromName: toTextWithPhonetics(a.countryCrossingFromName) }),\n ...(a.countryCrossingFromCodeIso2 && { countryCrossingFromCode: toIso3(a.countryCrossingFromCodeIso2) }),\n ...(a.countryCrossingToName && { countryCrossingToName: toTextWithPhonetics(a.countryCrossingToName) }),\n ...(a.countryCrossingToCodeIso2 && { countryCrossingToCode: toIso3(a.countryCrossingToCodeIso2) }),\n});\n\n// Optional maneuver detail fields, only present on the relevant maneuver kinds.\nconst parseManeuverExtras = (a: InstructionAPI): Partial<Instruction> => ({\n ...(a.intersectionName && { intersectionName: toTextWithPhonetics(a.intersectionName) }),\n ...(a.drivingSide && { drivingSide: a.drivingSide.toUpperCase() as DrivingSide }),\n ...(a.landmark && { landmark: V3_LANDMARK_MAP[a.landmark] ?? (a.landmark as Landmark) }),\n ...(!isNil(a.distanceToPreviousTrafficLightInMeters) && {\n trafficLightOffsetInMeters: a.distanceToPreviousTrafficLightInMeters,\n }),\n ...(a.sideRoads && { sideRoads: a.sideRoads }),\n ...(!isNil(a.isManeuverObligatory) && { isManeuverObligatory: a.isManeuverObligatory }),\n ...(!isNil(a.changeOfAngleInDegrees) && { changeOfAngleInDegrees: a.changeOfAngleInDegrees }),\n ...(!isNil(a.ambiguousExitOffsetFromManeuverInMeters) && {\n offsetOfAmbiguousExitFromManeuverInMeters: a.ambiguousExitOffsetFromManeuverInMeters,\n }),\n ...(!isNil(a.roundaboutExitNumber) && { roundaboutExitNumber: a.roundaboutExitNumber }),\n});\n\n// Road-shield related fields: signpost, the atlas base URL, and the flat reference lists. V2 exposed\n// a flat per-instruction road-shield list; V3 carries them under nextRoadInformation.\nconst parseRoadShieldFields = (a: InstructionAPI, roadShieldAtlasReference?: string): Partial<Instruction> => {\n const signpost = a.signpost ? parseSignpost(a.signpost) : undefined;\n const roadShieldReferences = (a.nextRoadInformation?.roadShields ?? [])\n .map((s) => s.iconReference)\n .filter((ref): ref is RoadShieldReference => !!ref);\n return {\n ...(signpost && { signpost }),\n ...(roadShieldAtlasReference && { roadShieldAtlasReference }),\n ...(roadShieldReferences.length && { roadShieldReferences }),\n ...(a.signpost?.exitIconReference && {\n signpostRoadShieldReferences: [a.signpost.exitIconReference],\n }),\n };\n};\n\nconst parseInstruction = (\n a: InstructionAPI,\n maneuverPoint: [number, number],\n pathPointIndex: number,\n roadShieldAtlasReference?: string,\n): Instruction => ({\n maneuverPoint,\n pathPointIndex,\n routeOffsetInMeters: a.routeOffsetInMeters,\n maneuver: (a.maneuver ? (V3_MANEUVER_MAP[a.maneuver] ?? a.maneuver) : a.maneuver) as Maneuver,\n routePath: (a.routePath ?? []).map((rp) => ({\n point: [rp.point.longitude, rp.point.latitude] as [number, number],\n distanceInMeters: rp.distanceFromRouteStartInMeters,\n travelTimeInSeconds: rp.travelTimeFromRouteStartInSeconds,\n })),\n nextRoadInfo: a.nextRoadInformation ? parseInstructionRoadInformation(a.nextRoadInformation) : EMPTY_ROAD_INFO,\n previousRoadInfo: a.previousRoadInformation\n ? parseInstructionRoadInformation(a.previousRoadInformation)\n : EMPTY_ROAD_INFO,\n ...parseManeuverExtras(a),\n ...parseRoadShieldFields(a, roadShieldAtlasReference),\n ...parseTollAndCrossing(a),\n});\n\nconst parseGuidance = (\n instructions: InstructionAPI[],\n path: Position[],\n roadShieldAtlasReference?: string,\n): Guidance => {\n const parsed: Instruction[] = [];\n let lastPathIndex = 0;\n\n for (const apiInstruction of instructions) {\n // V3 uses {latitude, longitude}, convert to GeoJSON [longitude, latitude] Position.\n const maneuverPoint: [number, number] = [\n apiInstruction.maneuverPoint.longitude,\n apiInstruction.maneuverPoint.latitude,\n ];\n\n for (let pathIndex = lastPathIndex; pathIndex < path.length; pathIndex++) {\n if (similar(path[pathIndex], maneuverPoint)) {\n lastPathIndex = pathIndex;\n break;\n }\n }\n\n parsed.push(parseInstruction(apiInstruction, maneuverPoint, lastPathIndex, roadShieldAtlasReference));\n }\n\n return { instructions: parsed };\n};\n\nconst parseRoute = (\n apiRoute: RouteAPI,\n index: number,\n params: CalculateRouteParams,\n roadShieldAtlasReference?: string,\n): Route => {\n const geometry = parseRoutePath(apiRoute.legs);\n return {\n type: 'Feature',\n geometry,\n id: generateId(),\n bbox: bboxFromGeoJSON(geometry) as BBox,\n properties: {\n index,\n summary: parseSummary(apiRoute.summary, params),\n sections: parseSections(apiRoute, params),\n ...(apiRoute.instructions?.length && {\n guidance: parseGuidance(apiRoute.instructions, geometry.coordinates, roadShieldAtlasReference),\n }),\n ...(apiRoute.progressPoints && {\n progress: apiRoute.progressPoints.map((progressPoint: ProgressPointAPI) => ({\n pointIndex: progressPoint.pathIndex,\n distanceInMeters: progressPoint.distanceInMeters,\n travelTimeInSeconds: progressPoint.travelDurationInSeconds,\n })),\n }),\n },\n };\n};\n\n/**\n * Default method for parsing calculate route response from {@link CalculateRouteResponseAPI}\n * @param apiResponse The Routing API response.\n * @param params The params used to calculate this route.\n */\nexport const parseCalculateRouteResponse = (\n apiResponse: CalculateRouteResponseAPI,\n params: CalculateRouteParams,\n): Routes => {\n const features = apiResponse.routes.map((apiRoute, index) =>\n parseRoute(apiRoute, index, params, apiResponse.roadShieldAtlasReference),\n );\n const bbox = bboxFromGeoJSON(features);\n return { type: 'FeatureCollection', ...(bbox && { bbox }), features };\n};\n","import type { Routes } from '@tomtom-org/maps-sdk/core';\nimport type { FetchInput, ServiceTemplate } from '../shared';\nimport { fetchWith } from '../shared/fetch';\nimport { routeRequestValidationConfig } from './calculateRouteRequestSchema';\nimport { buildCalculateRouteRequest } from './requestBuilder';\nimport { parseCalculateRouteResponse } from './responseParser';\nimport { parseRoutingResponseError } from './routingResponseErrorParser';\nimport type { CalculateRoutePOSTDataAPI } from './types/apiRequestTypes';\nimport type { CalculateRouteResponseAPI } from './types/apiResponseTypes';\nimport type { CalculateRouteParams } from './types/calculateRouteParams';\n\n/**\n * @ignore\n */\nexport type CalculateRouteTemplate = ServiceTemplate<\n CalculateRouteParams,\n FetchInput<CalculateRoutePOSTDataAPI>,\n CalculateRouteResponseAPI,\n Routes\n>;\n\n/**\n * @ignore\n */\nexport const calculateRouteTemplate: CalculateRouteTemplate = {\n requestValidation: routeRequestValidationConfig,\n buildRequest: buildCalculateRouteRequest,\n sendRequest: fetchWith,\n parseResponse: parseCalculateRouteResponse,\n parseResponseError: parseRoutingResponseError,\n getAPIVersion: () => 3,\n};\n","import type { CalculateRouteTemplate } from './calculateRouteTemplate';\nimport { calculateRouteTemplate } from './calculateRouteTemplate';\nimport { buildCalculateRouteRequest } from './requestBuilder';\nimport { parseCalculateRouteResponse } from './responseParser';\n\nconst customize: {\n buildCalculateRouteRequest: typeof buildCalculateRouteRequest;\n parseCalculateRouteResponse: typeof parseCalculateRouteResponse;\n calculateRouteTemplate: CalculateRouteTemplate;\n} = {\n buildCalculateRouteRequest,\n parseCalculateRouteResponse,\n calculateRouteTemplate,\n};\nexport default customize;\n","import type { AreaAnalyticsMetricKey } from '@tomtom-org/maps-sdk/core';\nimport type { MultiPolygon, Polygon } from 'geojson';\nimport type { CommonServiceParams } from '../../shared';\n\n// ---------------------------------------------------------------------------\n// Functional Road Classes\n// ---------------------------------------------------------------------------\n\n/**\n * Ordered list of Functional Road Class identifiers, from highest importance (motorways) to lowest.\n *\n * @remarks\n * The index of each class in this array corresponds to its FRC value in the API (0 for 'MOTORWAY', 1 for 'MAJOR_ROAD', ..., 8 for 'OTHER_ROAD').\n *\n * @see https://docs.tomtom.com/traffic-stats/documentation/product-information/faq#what-are-functional-road-classes-frc\n *\n * @group Traffic\n */\nexport const functionalRoadClasses = [\n 'MOTORWAY',\n 'MAJOR_ROAD',\n 'OTHER_MAJOR_ROAD',\n 'SECONDARY_ROAD',\n 'LOCAL_CONNECTING_ROAD',\n 'LOCAL_ROAD_HIGH_IMPORTANCE',\n 'LOCAL_ROAD',\n 'LOCAL_ROAD_MINOR_IMPORTANCE',\n 'OTHER_ROAD',\n] as const;\n\n/**\n * A Functional Road Class (FRC) identifier — classifies road segments by their importance\n * in the road network.\n *\n * | Value | Description |\n * |---|---|\n * | `'MOTORWAY'` | Motorways, freeways, and major roads |\n * | `'MAJOR_ROAD'` | Major roads, less important than motorways |\n * | `'OTHER_MAJOR_ROAD'` | Other major roads connecting neighbouring regions |\n * | `'SECONDARY_ROAD'` | Secondary roads linking parts of the same region |\n * | `'LOCAL_CONNECTING_ROAD'` | Local roads providing settlement access |\n * | `'LOCAL_ROAD_HIGH_IMPORTANCE'` | Local roads of high importance within a settlement |\n * | `'LOCAL_ROAD'` | Local roads within settlement sections |\n * | `'LOCAL_ROAD_MINOR_IMPORTANCE'` | Local roads of minor importance (dead-ends, alleys) |\n * | `'OTHER_ROAD'` | Other roads (paths, cycle routes, pedestrian infrastructure) |\n *\n * @group Traffic\n */\nexport type FunctionalRoadClass = (typeof functionalRoadClasses)[number];\n\n// ---------------------------------------------------------------------------\n// Metric keys\n// ---------------------------------------------------------------------------\n\n/**\n * Maps a camelCase {@link AreaAnalyticsMetricKey} to the UPPER_CASE string expected by the API.\n *\n * @internal\n */\nexport const metricKeyToApiDataType: Record<AreaAnalyticsMetricKey, string> = {\n speed: 'SPEED',\n freeFlowSpeed: 'FREE_FLOW_SPEED',\n congestionLevel: 'CONGESTION_LEVEL',\n travelTime: 'TRAVEL_TIME',\n networkLength: 'NETWORK_LENGTH',\n};\n\n// ---------------------------------------------------------------------------\n// Date input\n// ---------------------------------------------------------------------------\n\n/**\n * A date value accepted by Traffic Area Analytics — either a `Date` object or\n * an ISO `'YYYY-MM-DD'` string.\n * `Date` objects are automatically converted to `YYYY-MM-DD` format before\n * being sent to the API.\n *\n * @group Traffic\n */\nexport type AreaAnalyticsDateInput = string | Date;\n\n// ---------------------------------------------------------------------------\n// Date selection — exactly one variant must be supplied\n// ---------------------------------------------------------------------------\n\n/**\n * Continuous date-range variant: analyse traffic between `startDate` and `endDate`.\n *\n * - `endDate` is optional; when omitted it defaults to **today**.\n * - The range must not exceed **31 days**.\n * - Mutually exclusive with {@link TrafficAreaAnalyticsDays}.\n *\n * @group Traffic\n */\nexport type TrafficAreaAnalyticsDateRange = {\n /**\n * Start date of the analysis period.\n * Accepts a `Date` object or an ISO `'YYYY-MM-DD'` string.\n *\n * @example '2024-08-01'\n * @example new Date('2024-08-01')\n */\n startDate: AreaAnalyticsDateInput;\n\n /**\n * End date of the analysis period (inclusive).\n * Accepts a `Date` object or an ISO `'YYYY-MM-DD'` string.\n * When omitted, defaults to **today**.\n *\n * @remarks Must be within 31 days from `startDate`.\n *\n * @example '2024-08-07'\n * @example new Date('2024-08-07')\n */\n endDate?: AreaAnalyticsDateInput;\n\n days?: never;\n};\n\n/**\n * Specific-dates variant: analyse traffic on an explicit list of dates.\n *\n * Useful for non-consecutive dates such as \"all Mondays in August\".\n * Mutually exclusive with {@link TrafficAreaAnalyticsDateRange}.\n *\n * @group Traffic\n */\nexport type TrafficAreaAnalyticsDays = {\n /**\n * List of specific dates to include in the analysis.\n * Accepts `Date` objects or ISO `'YYYY-MM-DD'` strings.\n *\n * @example ['2024-08-05', '2024-08-12', '2024-08-19', '2024-08-26']\n */\n days: AreaAnalyticsDateInput[];\n\n startDate?: never;\n endDate?: never;\n};\n\n// ---------------------------------------------------------------------------\n// Main params type\n// ---------------------------------------------------------------------------\n\n/**\n * Parameters for the Traffic Area Analytics Lite service.\n *\n * @remarks\n * Supply **either** `startDate` (+ optional `endDate`) **or** `days` — these\n * two options are mutually exclusive.\n *\n * The lite endpoint runs analysis synchronously and returns results immediately,\n * with the constraint that the date range must not exceed 31 days and only a\n * single region may be submitted.\n *\n * @example\n * ```typescript\n * // Continuous range — endDate defaults to today when omitted\n * const result = await trafficAreaAnalytics({\n * startDate: '2024-08-01',\n * functionalRoadClasses: ['MOTORWAY', 'MAJOR_ROAD', 'OTHER_MAJOR_ROAD', 'SECONDARY_ROAD', 'LOCAL_CONNECTING_ROAD', 'LOCAL_ROAD_HIGH_IMPORTANCE'],\n * hours: [7, 8, 9, 17, 18],\n * metrics: ['speed', 'congestionLevel'],\n * geometry: {\n * type: 'Polygon',\n * coordinates: [[[4.896128, 52.382402], [4.875701, 52.368459], [4.923611, 52.36341], [4.896128, 52.382402]]]\n * }\n * });\n * ```\n *\n * @example\n * ```typescript\n * // Specific dates\n * const result = await trafficAreaAnalytics({\n * days: ['2024-08-05', '2024-08-12', '2024-08-19', '2024-08-26'],\n * functionalRoadClasses: ['MOTORWAY', 'MAJOR_ROAD', 'OTHER_MAJOR_ROAD'],\n * hours: [8, 9, 17, 18],\n * metrics: ['speed'],\n * geometry: polygon\n * });\n * ```\n *\n * @group Traffic\n */\nexport type TrafficAreaAnalyticsParams = CommonServiceParams &\n TrafficAreaAnalyticsBaseParams &\n (TrafficAreaAnalyticsDateRange | TrafficAreaAnalyticsDays);\n\n/**\n * @ignore\n */\ntype TrafficAreaAnalyticsBaseParams = {\n /**\n * Optional name for the analysis report.\n */\n name?: string;\n\n /**\n * Traffic metrics to include in the analysis, or `'all'` to include every metric.\n *\n * @remarks\n * At least one metric must be provided, or pass `'all'` as a shorthand for all five metrics.\n *\n * @example ['speed', 'congestionLevel', 'freeFlowSpeed']\n * @example 'all'\n */\n metrics: AreaAnalyticsMetricKey[] | 'all';\n\n /**\n * Functional road classes to include in the analysis, or `'all'` to include every class.\n *\n * @remarks\n * At least one value must be provided, or pass `'all'` as a shorthand for all nine classes.\n *\n * @example ['MOTORWAY', 'MAJOR_ROAD', 'OTHER_MAJOR_ROAD']\n * @example 'all'\n */\n functionalRoadClasses: FunctionalRoadClass[] | 'all';\n\n /**\n * Hours of the day to include in the analysis (0–23), or `'all'` to include every hour.\n *\n * @remarks\n * Provide at least one hour, or pass `'all'` as a shorthand for `[0, 1, 2, ..., 23]`.\n *\n * @example [7, 8, 9, 17, 18]\n * @example 'all'\n */\n hours: number[] | 'all';\n\n /**\n * The GeoJSON Polygon or MultiPolygon geometry defining the analysis region.\n *\n * @remarks\n * The lite endpoint accepts exactly one region. For multi-region analysis,\n * use the standard (async) report creation endpoint.\n */\n geometry: Polygon | MultiPolygon;\n};\n","import { areaAnalyticsMetricKeys } from '@tomtom-org/maps-sdk/core';\nimport type { FetchInput } from '../shared';\nimport { appendCommonParams } from '../shared/request/requestBuildingUtils';\nimport type { AreaAnalyticsRequestBody } from './types/apiTypes';\nimport type {\n AreaAnalyticsDateInput,\n FunctionalRoadClass,\n TrafficAreaAnalyticsParams,\n} from './types/trafficAreaAnalyticsParams';\nimport { functionalRoadClasses, metricKeyToApiDataType } from './types/trafficAreaAnalyticsParams';\n\nconst AREA_ANALYTICS_URL_PATH = '/areaanalytics/reports/lite';\n\nconst buildUrlBasePath = (params: TrafficAreaAnalyticsParams): string =>\n params.customServiceBaseURL ?? `${params.commonBaseURL}${AREA_ANALYTICS_URL_PATH}`;\n\nconst toDateString = (date: AreaAnalyticsDateInput): string =>\n // (ensure to get only YYYY-MM-DD portion of the dates)\n date instanceof Date ? date.toISOString().slice(0, 10) : date;\n\nconst ALL_HOURS = Array.from({ length: 24 }, (_, i) => i);\n\nconst frcToNumber = (frc: FunctionalRoadClass): number => functionalRoadClasses.indexOf(frc);\n\n/**\n * Default method for building a Traffic Area Analytics request from {@link TrafficAreaAnalyticsParams}.\n * @param params The traffic area analytics parameters, with global configuration already merged.\n */\nexport const buildTrafficAreaAnalyticsRequest = (\n params: TrafficAreaAnalyticsParams,\n): FetchInput<AreaAnalyticsRequestBody> => {\n const url = new URL(buildUrlBasePath(params));\n appendCommonParams(url.searchParams, params);\n\n const daysAgo = (numDays: number): Date => {\n const date = new Date();\n date.setDate(date.getDate() - numDays);\n return date;\n };\n\n const datePart = Array.isArray(params.days)\n ? { days: params.days.map(toDateString) }\n : {\n startDate: toDateString(params.startDate ?? daysAgo(3)),\n endDate: toDateString(params.endDate ?? daysAgo(2)),\n };\n\n const body: AreaAnalyticsRequestBody = {\n name: params.name ?? `Maps SDK JS Area Analytics Report ${new Date().toISOString()}`,\n ...datePart,\n dataTypes:\n params.metrics === 'all'\n ? areaAnalyticsMetricKeys.map((k) => metricKeyToApiDataType[k])\n : params.metrics.map((k) => metricKeyToApiDataType[k]),\n frcs:\n params.functionalRoadClasses === 'all'\n ? functionalRoadClasses.map((_, i) => i)\n : params.functionalRoadClasses.map(frcToNumber),\n hours: params.hours === 'all' ? ALL_HOURS : params.hours,\n features: [{ type: 'Feature', geometry: params.geometry }],\n };\n\n return { method: 'POST', url, data: body };\n};\n","import {\n AreaAnalyticsAnomaly,\n AreaAnalyticsFeature,\n AreaAnalyticsFeatureProperties,\n AreaAnalyticsMetricKey,\n AreaAnalyticsMetricRange,\n AreaAnalyticsMetrics,\n AreaAnalyticsTileEntry,\n AreaAnalyticsTimedData,\n AreaAnalyticsTimedEntry,\n areaAnalyticsMetricKeys,\n BBox,\n bboxFromGeoJSON,\n TrafficAreaAnalytics,\n} from '@tomtom-org/maps-sdk/core';\nimport type {\n AnomalyAPI,\n AreaAnalyticsFeatureAPI,\n AreaAnalyticsResponseAPI,\n FeaturePropertiesAPI,\n MetricsAPI,\n TiledEntryAPI,\n TimedEntryAPI,\n} from './types/apiTypes';\n\nconst parseMetrics = (api: MetricsAPI): AreaAnalyticsMetrics => ({\n ...(api.v !== undefined && { speed: api.v }),\n ...(api.fv !== undefined && { freeFlowSpeed: api.fv }),\n ...(api.c !== undefined && { congestionLevel: api.c }),\n ...(api.t !== undefined && { travelTime: api.t }),\n ...(api.l !== undefined && { networkLength: api.l }),\n});\n\nconst parseTimedEntry = (entry: TimedEntryAPI): AreaAnalyticsTimedEntry => ({\n ...(entry.date !== undefined && { date: new Date(entry.date) }),\n ...(entry.year !== undefined && { year: entry.year }),\n ...(entry.month !== undefined && { month: entry.month }),\n ...(entry.week !== undefined && { week: entry.week }),\n ...(entry.hour !== undefined && { hour: entry.hour }),\n ...(entry.day !== undefined && { day: entry.day }),\n ...parseMetrics(entry),\n});\n\n/**\n * Adds a given number of days to a Date, returning a new Date (no mutation).\n */\nconst addDays = (base: Date, days: number): Date => {\n const d = new Date(base);\n d.setDate(d.getDate() + days);\n return d;\n};\n\n/**\n * Parses hourly entries (sequential, 24 per day) and computes `date` + `hour`\n * from the sequential index when the API doesn't provide them.\n */\nconst parseHourlyEntries = (entries: TimedEntryAPI[], startDate: Date): AreaAnalyticsTimedEntry[] =>\n entries.map((entry, index) => {\n const base = parseTimedEntry(entry);\n // Only compute if the API didn't already provide the fields\n if (base.date === undefined) {\n base.date = addDays(startDate, Math.floor(index / 24));\n }\n if (base.hour === undefined) {\n base.hour = index % 24;\n }\n return base;\n });\n\n/**\n * Parses daily entries (sequential, one per day) and computes `date`\n * from the sequential index when the API doesn't provide it.\n */\nconst parseDailyEntries = (entries: TimedEntryAPI[], startDate: Date): AreaAnalyticsTimedEntry[] =>\n entries.map((entry, index) => {\n const base = parseTimedEntry(entry);\n if (base.date === undefined) {\n base.date = addDays(startDate, index);\n }\n return base;\n });\n\n/**\n * Parses average entries (sequential, 24 per day-of-week, 7 days) and computes\n * `day` (1=Monday…7=Sunday) + `hour` from the sequential index when the API\n * doesn't provide them.\n */\nconst parseAverageEntries = (entries: TimedEntryAPI[]): AreaAnalyticsTimedEntry[] =>\n entries.map((entry, index) => {\n const base = parseTimedEntry(entry);\n if (base.day === undefined) {\n base.day = Math.floor(index / 24) + 1; // 1 = Monday … 7 = Sunday\n }\n if (base.hour === undefined) {\n base.hour = index % 24;\n }\n return base;\n });\n\nconst parseTileEntry = (tile: TiledEntryAPI): AreaAnalyticsTileEntry => ({\n tileCentre: [tile.lon, tile.lat],\n ...parseMetrics(tile),\n});\n\nconst parseAnomaly = (api: AnomalyAPI): AreaAnalyticsAnomaly => ({\n startDate: new Date(api.startDate),\n endDate: new Date(api.endDate),\n labels: api.labels,\n});\n\nconst parseTimedData = (api: FeaturePropertiesAPI['timedData'], startDate: Date): AreaAnalyticsTimedData => ({\n ...(api.yearly && { yearly: api.yearly.map(parseTimedEntry) }),\n ...(api.monthly && { monthly: api.monthly.map(parseTimedEntry) }),\n ...(api.weekly && { weekly: api.weekly.map(parseTimedEntry) }),\n ...(api.daily && { daily: parseDailyEntries(api.daily, startDate) }),\n ...(api.hourly && { hourly: parseHourlyEntries(api.hourly, startDate) }),\n ...(api.average && { average: parseAverageEntries(api.average) }),\n});\n\nconst API_DATA_TYPE_TO_METRIC_KEY: Record<string, AreaAnalyticsMetricKey> = {\n SPEED: 'speed',\n FREE_FLOW_SPEED: 'freeFlowSpeed',\n CONGESTION_LEVEL: 'congestionLevel',\n TRAVEL_TIME: 'travelTime',\n NETWORK_LENGTH: 'networkLength',\n};\n\nconst parseAnomalies = (\n api: Record<string, AnomalyAPI[]>,\n): Partial<Record<AreaAnalyticsMetricKey, AreaAnalyticsAnomaly[]>> => {\n const result: Partial<Record<AreaAnalyticsMetricKey, AreaAnalyticsAnomaly[]>> = {};\n for (const [key, anomalies] of Object.entries(api)) {\n const metricKey = API_DATA_TYPE_TO_METRIC_KEY[key];\n if (metricKey) result[metricKey] = anomalies.map(parseAnomaly);\n }\n return result;\n};\n\nconst parseFeatureProperties = (api: FeaturePropertiesAPI, startDate: Date): AreaAnalyticsFeatureProperties => ({\n name: api.name,\n timezone: api.timezone,\n level: api.level,\n baseData: parseMetrics(api.baseData),\n timedData: parseTimedData(api.timedData, startDate),\n ...(api.tiledData && {\n tiledData: { tiles: api.tiledData.tiles.map(parseTileEntry) },\n }),\n ...(api.anomalies && {\n anomalies: parseAnomalies(api.anomalies),\n }),\n});\n\nconst parseFeature = (apiFeature: AreaAnalyticsFeatureAPI, startDate: Date): AreaAnalyticsFeature => ({\n type: apiFeature.type,\n id: apiFeature.id,\n geometry: apiFeature.geometry,\n properties: parseFeatureProperties(apiFeature.properties, startDate),\n bbox: bboxFromGeoJSON(apiFeature.geometry) as BBox,\n});\n\nconst computeTileRanges = (\n features: AreaAnalyticsFeature[],\n): Partial<Record<AreaAnalyticsMetricKey, AreaAnalyticsMetricRange>> => {\n const tiles = features.flatMap((f) => f.properties.tiledData?.tiles ?? []);\n const ranges: Partial<Record<AreaAnalyticsMetricKey, AreaAnalyticsMetricRange>> = {};\n for (const key of areaAnalyticsMetricKeys) {\n const values = tiles.map((t) => t[key]).filter((v): v is number => v != null);\n if (values.length > 0) {\n ranges[key] = { min: Math.min(...values), max: Math.max(...values) };\n }\n }\n return ranges;\n};\n\n/**\n * Parses a raw Traffic Area Analytics API response into the SDK representation.\n *\n * In addition to renaming abbreviated metric fields and converting date strings\n * to `Date` objects, this function computes data-driven min/max ranges from\n * all tile entries across every feature and attaches them to\n * `result.properties.ranges`.\n *\n * @param apiResponse - The raw Area Analytics API response.\n */\nexport const parseTrafficAreaAnalyticsResponse = (apiResponse: AreaAnalyticsResponseAPI): TrafficAreaAnalytics => {\n const startDate = new Date(apiResponse.properties.startDate);\n const features = apiResponse.features.map((f) => parseFeature(f, startDate));\n\n return {\n type: 'FeatureCollection',\n properties: {\n startDate,\n endDate: new Date(apiResponse.properties.endDate),\n metrics: apiResponse.properties.dataTypes\n .map((dt) => API_DATA_TYPE_TO_METRIC_KEY[dt])\n .filter((key): key is AreaAnalyticsMetricKey => key !== undefined),\n heatmap: apiResponse.properties.heatmap,\n frcs: apiResponse.properties.frcs,\n ranges: computeTileRanges(features),\n },\n features,\n };\n};\n","import { areaAnalyticsMetricKeys } from '@tomtom-org/maps-sdk/core';\nimport { z } from 'zod';\nimport { commonServiceRequestSchema } from '../shared/schema/commonParamsSchema';\nimport { functionalRoadClasses } from './types/trafficAreaAnalyticsParams';\n\nconst dateInputSchema = z.union([\n z.date(),\n z.string().regex(/^\\d{4}-\\d{2}-\\d{2}$/, \"Date must be in 'YYYY-MM-DD' format\"),\n]);\n\nconst geometrySchema = z.union([\n z.object({\n type: z.literal('Polygon'),\n coordinates: z.array(z.array(z.array(z.number()))).min(1),\n }),\n z.object({\n type: z.literal('MultiPolygon'),\n coordinates: z.array(z.array(z.array(z.array(z.number())))).min(1),\n }),\n]);\n\nconst toDateObj = (d: Date | string): Date => (d instanceof Date ? d : new Date(d));\n\nconst startOfDayUTC = (d: Date | string): number => {\n const date = toDateObj(d);\n return Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate());\n};\n\nconst ONE_DAY_MS = 24 * 60 * 60 * 1000;\n\nconst isAtLeastTwoDaysBeforeToday = (d: Date | string): boolean => {\n const now = new Date();\n const todayUTC = Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate());\n return startOfDayUTC(d) <= todayUTC - 2 * ONE_DAY_MS;\n};\n\n/**\n * @ignore\n */\nexport const trafficAreaAnalyticsRequestSchema = commonServiceRequestSchema\n .extend({\n name: z.string().optional(),\n startDate: dateInputSchema.optional(),\n endDate: dateInputSchema.optional(),\n days: z.array(dateInputSchema).min(1).optional(),\n metrics: z.union([z.array(z.enum(areaAnalyticsMetricKeys)).min(1), z.literal('all')]),\n functionalRoadClasses: z.union([z.array(z.enum(functionalRoadClasses)).min(1), z.literal('all')]),\n hours: z.union([z.array(z.number().int().min(0).max(23)).min(1), z.literal('all')]),\n geometry: geometrySchema,\n })\n .refine(\n (data) => {\n const hasDays = data.days !== undefined;\n const hasStart = data.startDate !== undefined;\n\n // Exactly one mode: either days OR startDate (+optional endDate)\n if (hasDays && hasStart) return false;\n if (!hasDays && !hasStart) return false;\n\n // Validate range for continuous range: startDate must be at least 1 day before endDate, within 31 days\n if (data.startDate !== undefined) {\n const effectiveEnd = data.endDate !== undefined ? toDateObj(data.endDate) : new Date();\n const diffDays = (effectiveEnd.getTime() - toDateObj(data.startDate).getTime()) / (1000 * 60 * 60 * 24);\n return diffDays >= 1 && diffDays <= 31;\n }\n\n return true;\n },\n {\n message:\n 'Provide either startDate (with optional endDate, at least 1 day apart and within 31 days) or days — but not both',\n },\n )\n .refine(\n (data) => {\n if (data.days !== undefined) {\n return data.days.every(isAtLeastTwoDaysBeforeToday);\n }\n\n const effectiveEnd = data.endDate ?? new Date();\n return isAtLeastTwoDaysBeforeToday(effectiveEnd);\n },\n {\n message: 'Dates must be at least 2 days before today',\n },\n );\n","import type { TrafficAreaAnalytics } from '@tomtom-org/maps-sdk/core';\nimport type { FetchInput, ServiceTemplate } from '../shared';\nimport { fetchWith } from '../shared/fetch';\nimport { buildTrafficAreaAnalyticsRequest } from './requestBuilder';\nimport { parseTrafficAreaAnalyticsResponse } from './responseParser';\nimport { trafficAreaAnalyticsRequestSchema } from './trafficAreaAnalyticsRequestSchema';\nimport type { AreaAnalyticsRequestBody, AreaAnalyticsResponseAPI } from './types/apiTypes';\nimport type { TrafficAreaAnalyticsParams } from './types/trafficAreaAnalyticsParams';\n\n/**\n * Traffic Area Analytics service template type.\n * @ignore\n */\nexport type TrafficAreaAnalyticsTemplate = ServiceTemplate<\n TrafficAreaAnalyticsParams,\n FetchInput<AreaAnalyticsRequestBody>,\n AreaAnalyticsResponseAPI,\n TrafficAreaAnalytics\n>;\n\n/**\n * Traffic Area Analytics service template main implementation.\n * @ignore\n */\nexport const trafficAreaAnalyticsTemplate: TrafficAreaAnalyticsTemplate = {\n requestValidation: { schema: trafficAreaAnalyticsRequestSchema },\n buildRequest: buildTrafficAreaAnalyticsRequest,\n sendRequest: fetchWith,\n parseResponse: parseTrafficAreaAnalyticsResponse,\n};\n","import { buildTrafficAreaAnalyticsRequest } from './requestBuilder';\nimport { parseTrafficAreaAnalyticsResponse } from './responseParser';\nimport type { TrafficAreaAnalyticsTemplate } from './trafficAreaAnalyticsTemplate';\nimport { trafficAreaAnalyticsTemplate } from './trafficAreaAnalyticsTemplate';\n\nconst customize: {\n buildTrafficAreaAnalyticsRequest: typeof buildTrafficAreaAnalyticsRequest;\n parseTrafficAreaAnalyticsResponse: typeof parseTrafficAreaAnalyticsResponse;\n trafficAreaAnalyticsTemplate: TrafficAreaAnalyticsTemplate;\n} = {\n buildTrafficAreaAnalyticsRequest,\n parseTrafficAreaAnalyticsResponse,\n trafficAreaAnalyticsTemplate,\n};\nexport default customize;\n","import { bboxFromGeoJSON, trafficIncidentToIconCategory } from '@tomtom-org/maps-sdk/core';\nimport type { FetchInput } from '../shared';\nimport {\n appendByJoiningParamValue,\n appendCommonParams,\n appendOptionalParam,\n} from '../shared/request/requestBuildingUtils';\nimport type { TrafficIncidentDetailsParams } from './types/trafficIncidentDetailsParams';\n\nconst TRAFFIC_URL_PATH = '/maps/orbis/traffic';\n\n// Request all available incident fields by default so callers get a complete response without\n// having to manually specify a projection string.\nconst DEFAULT_FIELDS =\n '{incidents{type,geometry{type,coordinates},properties{id,iconCategory,magnitudeOfDelay,events{description,code,iconCategory},startTime,endTime,from,to,length,delay,roadNumbers,timeValidity,probabilityOfOccurrence,numberOfReports,lastReportTime,tmc{countryCode,tableNumber,tableVersion,direction,points{location,offset}}}}}';\n\ntype TrafficIncidentDetailsPostBody = { ids: string[] };\n\nconst buildUrlBasePath = (params: TrafficIncidentDetailsParams): string =>\n params.customServiceBaseURL ?? `${params.commonBaseURL}${TRAFFIC_URL_PATH}/incidentDetails`;\n\nconst appendOptionalParams = (urlParams: URLSearchParams, params: TrafficIncidentDetailsParams): void => {\n appendOptionalParam(urlParams, 'fields', DEFAULT_FIELDS);\n appendOptionalParam(urlParams, 't', params.trafficModelId);\n appendByJoiningParamValue(urlParams, 'categoryFilter', params.categoryFilter?.map(trafficIncidentToIconCategory));\n appendByJoiningParamValue(urlParams, 'timeValidityFilter', params.timeValidityFilter);\n};\n\n/**\n * Default method for building a traffic incident details request from {@link TrafficIncidentDetailsParams}.\n * @param params The traffic incident details parameters, with global configuration already merged into them.\n */\nexport const buildTrafficIncidentDetailsRequest = (\n params: TrafficIncidentDetailsParams,\n): FetchInput<TrafficIncidentDetailsPostBody> => {\n const url = new URL(buildUrlBasePath(params));\n const urlParams = url.searchParams;\n appendCommonParams(urlParams, params);\n appendOptionalParams(urlParams, params);\n\n if ('bbox' in params && params.bbox) {\n const resolvedBBox = bboxFromGeoJSON(params.bbox);\n if (resolvedBBox) {\n urlParams.append('bbox', resolvedBBox.join(','));\n }\n return { method: 'GET', url };\n }\n\n // ids-based request — POST when there are more than 5 IDs (GET limit), GET otherwise\n const { ids } = params as { ids: string[] };\n\n if (ids.length > 5) {\n return { method: 'POST', url, data: { ids } };\n }\n\n urlParams.append('ids', ids.join(','));\n return { method: 'GET', url };\n};\n","import {\n iconToTrafficIncidentCategory,\n indexedMagnitudes,\n type TrafficIncident,\n type TrafficIncidentDetails,\n type TrafficIncidentEvent,\n type TrafficIncidentProbability,\n type TrafficIncidentTimeValidity,\n type TrafficIncidentTMC,\n} from '@tomtom-org/maps-sdk/core';\nimport type { IncidentAPI, IncidentDetailsResponseAPI, IncidentEventAPI, IncidentTMCAPI } from './types/apiTypes';\n\nconst parseEvent = (apiEvent: IncidentEventAPI): TrafficIncidentEvent => ({\n description: apiEvent.description,\n code: apiEvent.code,\n category: iconToTrafficIncidentCategory(apiEvent.iconCategory),\n});\n\nconst parseTMC = (apiTMC: IncidentTMCAPI): TrafficIncidentTMC => ({\n countryCode: apiTMC.countryCode,\n tableNumber: apiTMC.tableNumber,\n tableVersion: apiTMC.tableVersion,\n direction: apiTMC.direction as 'positive' | 'negative',\n points: (apiTMC.points ?? []).map((p) => ({\n location: p.location,\n ...(p.offset != null && { offset: p.offset }),\n })),\n});\n\nconst parseIncident = (apiIncident: IncidentAPI): TrafficIncident => {\n const p = apiIncident.properties;\n return {\n type: 'Feature',\n // Top-level id is what the SDK's event-state machinery uses to track per-feature\n // click/hover state (see `renderedRefId` in map's featureId.ts). promoteId: 'id' on the\n // source keeps rendered features pointing at the same identifier.\n id: p.id,\n geometry: apiIncident.geometry,\n properties: {\n id: p.id,\n category: iconToTrafficIncidentCategory(p.iconCategory),\n magnitudeOfDelay: indexedMagnitudes[p.magnitudeOfDelay] ?? 'unknown',\n events: (p.events ?? []).map(parseEvent),\n ...(p.startTime && { startTime: new Date(p.startTime) }),\n ...(p.endTime && { endTime: new Date(p.endTime) }),\n ...(p.from && { from: p.from }),\n ...(p.to && { to: p.to }),\n // The API can return `length` / `delay` as an explicit `null` (e.g. closures and\n // roadworks carry no delay). `!= null` drops both `null` and `undefined` so these\n // optional numeric fields are omitted rather than set to `null` — a `null` here\n // violates the `number | undefined` type and trips MapLibre's numeric paint/filter\n // expressions (\"Expected value to be of type number, but found null instead\").\n ...(p.length != null && { lengthInMeters: p.length }),\n ...(p.delay != null && { delayInSeconds: p.delay }),\n ...(p.roadNumbers && { roadNumbers: p.roadNumbers }),\n timeValidity: p.timeValidity as TrafficIncidentTimeValidity,\n ...(p.probabilityOfOccurrence && {\n probabilityOfOccurrence: p.probabilityOfOccurrence as TrafficIncidentProbability,\n }),\n ...(p.numberOfReports != null && { numberOfReports: p.numberOfReports }),\n ...(p.lastReportTime && { lastReportTime: new Date(p.lastReportTime) }),\n ...(p.tmc && { tmc: parseTMC(p.tmc) }),\n },\n };\n};\n\n/**\n * Default method for parsing a traffic incident details API response.\n * @param apiResponse The raw Traffic Incident Details API response.\n */\nexport const parseTrafficIncidentDetailsResponse = (\n apiResponse: IncidentDetailsResponseAPI,\n): TrafficIncidentDetails => ({\n type: 'FeatureCollection',\n features: (apiResponse.incidents ?? []).filter((i): i is IncidentAPI => i !== null).map(parseIncident),\n});\n","import { trafficIncidentRequestCategories } from '@tomtom-org/maps-sdk/core';\nimport { z } from 'zod';\nimport { commonServiceRequestSchema } from '../shared/schema/commonParamsSchema';\nimport { hasBBoxSchema } from '../shared/schema/geometriesSchema';\n\n/**\n * @ignore\n */\nexport const trafficIncidentDetailsRequestSchema = commonServiceRequestSchema\n .extend({\n bbox: hasBBoxSchema.optional(),\n ids: z.array(z.string()).optional(),\n trafficModelId: z.string().optional(),\n categoryFilter: z.array(z.enum(trafficIncidentRequestCategories)).optional(),\n timeValidityFilter: z.array(z.enum(['present', 'future'])).optional(),\n })\n .refine((data) => !(data.bbox && data.ids), {\n message: 'Provide either bbox or ids, not both',\n });\n","import type { TrafficIncidentDetails } from '@tomtom-org/maps-sdk/core';\nimport type { FetchInput, ServiceTemplate } from '../shared';\nimport { fetchWith } from '../shared/fetch';\nimport { buildTrafficIncidentDetailsRequest } from './requestBuilder';\nimport { parseTrafficIncidentDetailsResponse } from './responseParser';\nimport { trafficIncidentDetailsRequestSchema } from './trafficIncidentDetailsRequestSchema';\nimport type { IncidentDetailsResponseAPI } from './types/apiTypes';\nimport type { TrafficIncidentDetailsParams } from './types/trafficIncidentDetailsParams';\n\ntype TrafficIncidentDetailsPostBody = { ids: string[] };\n\n/**\n * Traffic Incident Details service template type.\n * @ignore\n */\nexport type TrafficIncidentDetailsTemplate = ServiceTemplate<\n TrafficIncidentDetailsParams,\n FetchInput<TrafficIncidentDetailsPostBody>,\n IncidentDetailsResponseAPI,\n TrafficIncidentDetails\n>;\n\n/**\n * Traffic Incident Details service template main implementation.\n * @ignore\n */\nexport const trafficIncidentDetailsTemplate: TrafficIncidentDetailsTemplate = {\n requestValidation: { schema: trafficIncidentDetailsRequestSchema },\n buildRequest: buildTrafficIncidentDetailsRequest,\n sendRequest: fetchWith,\n parseResponse: parseTrafficIncidentDetailsResponse,\n};\n","import { buildTrafficIncidentDetailsRequest } from './requestBuilder';\nimport { parseTrafficIncidentDetailsResponse } from './responseParser';\nimport type { TrafficIncidentDetailsTemplate } from './trafficIncidentDetailsTemplate';\nimport { trafficIncidentDetailsTemplate } from './trafficIncidentDetailsTemplate';\n\nconst customize: {\n buildTrafficIncidentDetailsRequest: typeof buildTrafficIncidentDetailsRequest;\n parseTrafficIncidentDetailsResponse: typeof parseTrafficIncidentDetailsResponse;\n trafficIncidentDetailsTemplate: TrafficIncidentDetailsTemplate;\n} = {\n buildTrafficIncidentDetailsRequest,\n parseTrafficIncidentDetailsResponse,\n trafficIncidentDetailsTemplate,\n};\nexport default customize;\n","/**\n * @module services-customization\n * @group Customization\n */\n\nimport alongRouteSearchCustomize from '../along-route-search/customize';\nimport autocompleteCustomize from '../autocomplete-search/customize';\nimport evChargingStationsAvailabilityCustomize from '../ev-charging-stations-availability/customize';\nimport geocodeCustomize from '../geocode/customize';\nimport geometryDataCustomize from '../geometry-data/customize';\nimport geometrySearchCustomize from '../geometry-search/customize';\nimport placeByIdCustomize from '../place-by-id/customize';\nimport reachableRangeCustomize from '../reachable-range/customize';\nimport revgeoCustomize from '../revgeo/customize';\nimport routingCustomize from '../routing/customize';\nimport trafficAreaAnalyticsCustomize from '../traffic-area-analytics/customize';\nimport trafficIncidentDetailsCustomize from '../traffic-incident-details/customize';\n\n/**\n * Access to service implementation components for advanced customization.\n *\n * This object provides low-level access to the internal components of each service,\n * allowing developers to customize request building, response parsing, and other\n * aspects of service behavior. This is useful for advanced use cases like:\n * - Custom request/response transformations\n * - Integration with custom API gateways or proxies\n * - Adding custom validation or error handling\n * - Implementing request/response logging or monitoring\n * - Adapting to custom API endpoints or versions\n *\n * @remarks\n * Most developers won't need to use this directly. The standard service functions\n * (like `geocode`, `search`, `calculateRoute`) are sufficient for typical use cases.\n * Only use customization when you need to modify the internal service behavior.\n *\n * Each service exposes:\n * - Request builders: Functions that construct API requests\n * - Response parsers: Functions that transform API responses\n * - Templates: Configuration objects defining service behavior\n * - Validation schemas: Input parameter validation rules\n *\n * @example\n * ```typescript\n * import { mergeFromGlobal } from '@tomtom-org/maps-sdk/core';\n * import { customizeService } from '@tomtom-org/maps-sdk/services';\n *\n * const { buildGeocodingRequest, parseGeocodingResponse } = customizeService.geocode;\n *\n * // Request builders expect the global configuration to be merged in already\n * const url = buildGeocodingRequest(mergeFromGlobal({ query: 'Amsterdam' }));\n *\n * // Send the request yourself, then parse the raw API payload\n * const apiResponse = await fetch(url);\n * const places = parseGeocodingResponse(await apiResponse.json());\n * ```\n *\n * @group Advanced\n */\nexport const customizeService: {\n alongRouteSearch: typeof alongRouteSearchCustomize;\n reverseGeocode: typeof revgeoCustomize;\n geocode: typeof geocodeCustomize;\n geometryData: typeof geometryDataCustomize;\n geometrySearch: typeof geometrySearchCustomize;\n calculateRoute: typeof routingCustomize;\n reachableRange: typeof reachableRangeCustomize;\n evChargingStationsAvailability: typeof evChargingStationsAvailabilityCustomize;\n trafficAreaAnalytics: typeof trafficAreaAnalyticsCustomize;\n trafficIncidentDetails: typeof trafficIncidentDetailsCustomize;\n placeByID: typeof placeByIdCustomize;\n autocompleteSearch: typeof autocompleteCustomize;\n} = {\n alongRouteSearch: alongRouteSearchCustomize,\n reverseGeocode: revgeoCustomize,\n geocode: geocodeCustomize,\n geometryData: geometryDataCustomize,\n geometrySearch: geometrySearchCustomize,\n calculateRoute: routingCustomize,\n reachableRange: reachableRangeCustomize,\n evChargingStationsAvailability: evChargingStationsAvailabilityCustomize,\n trafficAreaAnalytics: trafficAreaAnalyticsCustomize,\n trafficIncidentDetails: trafficIncidentDetailsCustomize,\n placeByID: placeByIdCustomize,\n autocompleteSearch: autocompleteCustomize,\n};\n","import type {\n ChargingPark,\n ChargingParkWithAvailability,\n ChargingStationsAvailability,\n CommonPlaceProps,\n EVChargingStationWithAvailabilityPlaceProps,\n Place,\n Places,\n} from '@tomtom-org/maps-sdk/core';\nimport { bboxFromGeoJSON } from '@tomtom-org/maps-sdk/core';\nimport { callService } from '../shared/serviceTemplate';\nimport type { EVChargingStationsAvailabilityTemplate } from './evChargingStationsAvailabilityTemplate';\nimport { evChargingStationsAvailabilityTemplate } from './evChargingStationsAvailabilityTemplate';\nimport type { ChargingStationsAvailabilityParams } from './types/evChargingStationsAvailabilityParams';\n\n/**\n * Get real-time availability of electric vehicle charging stations.\n *\n * Provides current operational status of charging points and connectors at EV charging parks,\n * enabling drivers to find available chargers before arriving at a location.\n *\n * @remarks\n * Key information returned:\n * - **Point-level status**: Available, Occupied, Reserved, Out of Service\n * - **Connector details**: Power ratings, plug types, current availability\n * - **Aggregated counts**: Quick overview of available vs occupied chargers\n * - **Access information**: Public, private, or restricted access\n * - **Opening hours**: When the charging facility is accessible\n *\n * @param params - Charging availability parameters with station ID\n * @param customTemplate - Advanced customization for request/response handling\n *\n * @returns Promise resolving to charging station availability information\n *\n * @example\n * ```typescript\n * // Get availability for a specific charging park\n * const availability = await evChargingStationsAvailability({\n * key: 'your-api-key',\n * id: 'charging-park-id-123'\n * });\n *\n * // Check how many chargers are available\n * const availableCount = availability.chargingPointAvailability.statusCounts.Available;\n * console.log(`${availableCount} chargers available`);\n *\n * // Find available CCS connectors\n * const ccsConnectors = availability.connectorAvailabilities.find(\n * ca => ca.connector.type === 'IEC62196Type2CCS'\n * );\n * ```\n *\n * @see [EV Charging Availability API](https://docs.tomtom.com/search-api/documentation)\n * @see [Places Quickstart Guide](https://docs.tomtom.com/maps-sdk-js/guides/services/places/quickstart)\n * @see [EV Charging Stations Availability Guide](https://docs.tomtom.com/maps-sdk-js/guides/services/places/ev-charging-stations-availability)\n *\n * @group EV Charging\n */\nexport const evChargingStationsAvailability = async (\n params: ChargingStationsAvailabilityParams,\n customTemplate?: Partial<EVChargingStationsAvailabilityTemplate>,\n): Promise<ChargingStationsAvailability | undefined> =>\n callService(\n params,\n { ...evChargingStationsAvailabilityTemplate, ...customTemplate },\n 'EVChargingStationsAvailability',\n );\n\n/**\n * Enhance a place with real-time EV charging availability data.\n *\n * Fetches availability information for an EV charging station and merges it into\n * the place properties.\n *\n * @param place - The place to enhance with availability data\n * @param commonParams - Common service parameters (e.g. `key`, `customServiceBaseURL`, `onAPIRequest`) forwarded to the availability request\n *\n * @returns Promise resolving to the place with merged availability information\n * @returns undefined if no availability data is present for the `place` input parameter.\n *\n * @example\n * ```typescript\n * // After search, enhance place with availability\n * const searchResult = await search({ query: 'EV charging', ... });\n * const place = searchResult.features[0];\n *\n * const enhancedPlace = await getPlaceWithEVAvailability(place);\n * const availability = enhancedPlace.properties.chargingPark?.availability;\n *\n * if (availability) {\n * console.log('Available chargers:', availability.chargingPointAvailability.count);\n * }\n * ```\n *\n * @group EV Charging\n */\nexport const getPlaceWithEVAvailability = async <P extends CommonPlaceProps = CommonPlaceProps>(\n place: Place<P>,\n commonParams?: Omit<ChargingStationsAvailabilityParams, 'id'>,\n): Promise<Place<EVChargingStationWithAvailabilityPlaceProps> | undefined> => {\n const availabilityId = place.properties.dataSources?.chargingAvailability?.id;\n if (!availabilityId) {\n return undefined;\n }\n try {\n const availability = await evChargingStationsAvailability({ ...commonParams, id: availabilityId });\n const poi = place.properties.poi;\n return availability\n ? {\n ...place,\n properties: {\n ...place.properties,\n // We override poi opening hours with the ones from the EV call, which might be better supported:\n ...(poi && { poi: { ...poi, openingHours: availability.openingHours } }),\n chargingPark: {\n ...(place.properties.chargingPark as ChargingPark),\n availability,\n },\n },\n }\n : undefined;\n } catch (e) {\n // (Likely a QPS limit error)\n console.error(e);\n return undefined;\n }\n};\n\n/**\n * Enhance multiple places with real-time EV charging availability data.\n *\n * Fetches availability information for all EV charging stations in a collection\n * and merges it into their properties. Non-EV places and EV stations without availability data are returned unchanged.\n *\n * @remarks\n * **Important**: Availability requests are made sequentially to avoid exceeding\n * API rate limits (QPS - Queries Per Second). For large result sets, this may\n * take some time.\n *\n * @param places - Collection of places to enhance\n * @param options - Configuration options, including common service parameters (e.g. `key`, `customServiceBaseURL`, `onAPIRequest`) forwarded to each availability request\n *\n * @returns Promise resolving to places collection with merged availability\n *\n * @example\n * ```typescript\n * // Search for charging stations and add availability\n * const results = await search({\n * query: 'EV charging',\n * at: [4.9, 52.3],\n * radius: 5000\n * });\n *\n * const withAvailability = await getPlacesWithEVAvailability(results, {\n * excludeIfAvailabilityUnknown: true // Filter out stations with unknown availability\n * });\n *\n * // Display only stations with known availability\n * withAvailability.features.forEach(place => {\n * const available = place.properties.chargingPark?.availability?.chargingPointAvailability.count;\n * console.log(`${place.properties.poi?.name}: ${available} chargers`);\n * });\n * ```\n *\n * @group EV Charging\n */\nexport async function getPlacesWithEVAvailability<P extends CommonPlaceProps = CommonPlaceProps>(\n places: Places<P>,\n options?: Omit<ChargingStationsAvailabilityParams, 'id'> & {\n excludeIfAvailabilityUnknown: true;\n },\n): Promise<Places<EVChargingStationWithAvailabilityPlaceProps>>;\n\nexport async function getPlacesWithEVAvailability<P extends CommonPlaceProps = CommonPlaceProps>(\n places: Places<P>,\n options?: Omit<ChargingStationsAvailabilityParams, 'id'> & {\n /**\n * If true, places with unknown availability will be filtered out. Otherwise, they will be included.\n * @default false\n */\n excludeIfAvailabilityUnknown?: false;\n },\n): Promise<Places<P | EVChargingStationWithAvailabilityPlaceProps>>;\n\nexport async function getPlacesWithEVAvailability<P extends CommonPlaceProps = CommonPlaceProps>(\n places: Places<P>,\n options?: Omit<ChargingStationsAvailabilityParams, 'id'> & {\n excludeIfAvailabilityUnknown?: boolean;\n },\n): Promise<Places<P | EVChargingStationWithAvailabilityPlaceProps>> {\n const { excludeIfAvailabilityUnknown, ...commonParams } = options ?? {};\n const enhancedPlaces: Array<Place<P> | Place<EVChargingStationWithAvailabilityPlaceProps>> = [];\n for (const place of places.features) {\n // (We fetch the availabilities sequentially on purpose to prevent QPS limit errors)\n const placeWithAvailability = await getPlaceWithEVAvailability(place, commonParams);\n if (placeWithAvailability) {\n enhancedPlaces.push(placeWithAvailability);\n } else if (!excludeIfAvailabilityUnknown) {\n enhancedPlaces.push(place);\n }\n }\n return { ...places, features: enhancedPlaces, bbox: bboxFromGeoJSON(enhancedPlaces) };\n}\n\n/**\n * Type guard to check if a ChargingPark has availability data.\n *\n * @param chargingPark - The charging park to check\n * @returns True if the charging park has availability data\n *\n * @group EV Charging\n */\nexport const hasChargingAvailability = (\n chargingPark: ChargingPark | ChargingParkWithAvailability | undefined,\n): chargingPark is ChargingParkWithAvailability =>\n Boolean(chargingPark && 'availability' in chargingPark && chargingPark.availability);\n","import { z } from 'zod';\nimport { commonSearchParamsSchema } from '../search/commonSearchParamsSchema';\nimport { commonGeocodeAndFuzzySearchParamsSchema } from '../shared/schema/commonGeocodeAndFuzzySearchParamsSchema';\nimport type { SchemaRefinement } from '../shared/types/validation';\nimport type { ExplorationSearchParams } from './types';\n\nconst positionSchema = z.array(z.number()).min(2);\n\nconst polygonSchema = z.object({\n type: z.literal('Polygon'),\n coordinates: z.array(z.array(z.array(z.number()))),\n bbox: z.array(z.number()).optional(),\n});\n\nconst multiPolygonSchema = z.object({\n type: z.literal('MultiPolygon'),\n coordinates: z.array(z.array(z.array(z.array(z.number())))),\n bbox: z.array(z.number()).optional(),\n});\n\nconst circleSchema = z.object({\n type: z.literal('Circle'),\n coordinates: positionSchema,\n radius: z.number().positive(),\n});\n\nconst polygonFeaturesSchema = z.object({\n type: z.literal('FeatureCollection'),\n features: z.array(\n z.object({\n type: z.literal('Feature'),\n geometry: z.union([polygonSchema, multiPolygonSchema]),\n properties: z.any().optional(),\n }),\n ),\n});\n\nconst geometryInputSchema = z.union([polygonSchema, multiPolygonSchema, circleSchema, polygonFeaturesSchema]);\n\nconst bboxTupleSchema = z.tuple([z.number(), z.number(), z.number(), z.number()]);\n\nconst hasBBoxObjectSchema = z.object({ bbox: bboxTupleSchema });\n\nconst hasBBoxSchema = z.union([bboxTupleSchema, hasBBoxObjectSchema]);\n\n// The places-api accepts from + size ≤ 10000 (OpenSearch's `index.max_result_window`),\n// so the exploration-search pagination bounds are wider than the generic TomTom search cap.\nconst PLACES_API_MAX_WINDOW = 10000;\n\n// When the request omits `size`, the places-api falls back to OpenSearch's default page size of 10,\n// so `from` alone still consumes 10 of the window. The refinement uses this to model wire behaviour.\nconst PLACES_API_DEFAULT_SIZE = 10;\n\nconst recordTypeSchema = z.enum(['POI', 'PointAddress', 'Street']);\n\nconst explorationSearchRequestOptional = z.object({\n municipalities: z.array(z.string()).optional(),\n boundingBoxes: z.array(hasBBoxSchema).optional(),\n geometries: z.array(geometryInputSchema).optional(),\n placeTypes: z.array(recordTypeSchema).min(1).optional(),\n areaId: z.string().min(1).optional(),\n areaTags: z.array(z.string().min(1)).min(1).optional(),\n offset: z.number().int().min(0).max(PLACES_API_MAX_WINDOW).optional(),\n limit: z.number().int().min(1).max(PLACES_API_MAX_WINDOW).optional(),\n});\n\n/**\n * @ignore\n */\nexport const explorationSearchRequestSchema = commonSearchParamsSchema.extend(\n commonGeocodeAndFuzzySearchParamsSchema.extend(explorationSearchRequestOptional.shape).shape,\n);\n\n// The places-api rejects fully unbiased searches (they fan out across the whole index),\n// so the SDK mirrors that contract by requiring at least one geographic filter.\n// `municipalities` and `areaId` are accepted as standalone biases — both pin\n// the response to a specific municipality polygon, no bbox/position needed.\nconst geoBiasRefinement: SchemaRefinement<ExplorationSearchParams> = {\n check: (data) =>\n !!data.position ||\n !!data.boundingBox ||\n !!data.boundingBoxes?.length ||\n !!data.geometries?.length ||\n !!data.municipalities?.length ||\n !!data.areaId,\n message:\n 'A geographic bias is required: provide one of position, boundingBox(es), geometries, municipalities, or areaId.',\n};\n\n// The places-api enforces `from + size ≤ 10000` (OpenSearch's\n// `index.max_result_window`). When `limit` is omitted the wire request also omits `size`,\n// and the backend falls back to its default page size — so the effective `size` on the\n// wire is `limit ?? PLACES_API_DEFAULT_SIZE`, not 0.\nconst paginationWindowRefinement: SchemaRefinement<ExplorationSearchParams> = {\n check: (data) => (data.offset ?? 0) + (data.limit ?? PLACES_API_DEFAULT_SIZE) <= PLACES_API_MAX_WINDOW,\n message: `offset + limit must not exceed ${PLACES_API_MAX_WINDOW} (the places-api pagination window; when limit is omitted the backend defaults to ${PLACES_API_DEFAULT_SIZE}).`,\n};\n\n/**\n * @ignore\n */\nexport const explorationSearchRequestValidationConfig = {\n schema: explorationSearchRequestSchema,\n refinements: [geoBiasRefinement, paginationWindowRefinement],\n};\n","import { type BBox, bboxFromGeoJSON, getPosition, poiCategoriesToID } from '@tomtom-org/maps-sdk/core';\nimport type { MultiPolygon, Polygon, Position } from 'geojson';\nimport type { Circle, SearchGeometryInput } from '../geometry-search';\nimport type { ExplorationSearchParams, ExplorationSearchPayloadAPI, ExplorationSearchRequestAPI } from './types';\n\nconst DEFAULT_NEAR_RADIUS_KM = 2;\nconst CIRCLE_POLYGON_SAMPLES = 64;\nconst EARTH_RADIUS_M = 6371000;\n\n// Experimental places-api deployment — keeping this URL hardcoded until the service graduates.\n// const EXPLORATION_SEARCH_BASE_URL = 'https://search-places-poc.braveriver-6e85d43a.westeurope.azurecontainerapps.io';\nconst EXPLORATION_SEARCH_BASE_URL = 'https://search-places-poc.whitebush-bbe9c5f7.westeurope.azurecontainerapps.io';\n\nconst buildUrlBasePath = (params: ExplorationSearchParams): string =>\n params.customServiceBaseURL ?? EXPLORATION_SEARCH_BASE_URL;\n\nconst toNearPayload = (params: ExplorationSearchParams): ExplorationSearchPayloadAPI['near'] => {\n const coordinates = getPosition(params.position);\n if (!coordinates) return undefined;\n const radiusKm =\n params.radiusMeters && params.radiusMeters > 0 ? params.radiusMeters / 1000 : DEFAULT_NEAR_RADIUS_KM;\n return { coordinates, radius_km: radiusKm };\n};\n\nconst toCategoriesPayload = (params: ExplorationSearchParams): string[] | undefined =>\n params.poiCategories?.length\n ? params.poiCategories.map((category) => String(poiCategoriesToID[category]))\n : undefined;\n\nconst toBboxesPayload = (params: ExplorationSearchParams): BBox[] | undefined => {\n const inputs = [...(params.boundingBox ? [params.boundingBox] : []), ...(params.boundingBoxes ?? [])];\n const bboxes = inputs.map((input) => bboxFromGeoJSON(input)).filter((bbox): bbox is BBox => bbox !== undefined);\n return bboxes.length ? bboxes : undefined;\n};\n\n// Samples a circle boundary into an equirectangular-approximation Polygon ring\n// so Circle inputs can be sent to a places API that only accepts GeoJSON polygons.\nconst circleToPolygon = (circle: Circle): Polygon => {\n const [lon, lat] = circle.coordinates;\n const dLatDeg = (circle.radius / EARTH_RADIUS_M) * (180 / Math.PI);\n const dLonDeg = dLatDeg / Math.cos((lat * Math.PI) / 180);\n const ring: Position[] = [];\n for (let i = 0; i < CIRCLE_POLYGON_SAMPLES; i++) {\n const angle = (2 * Math.PI * i) / CIRCLE_POLYGON_SAMPLES;\n ring.push([lon + dLonDeg * Math.cos(angle), lat + dLatDeg * Math.sin(angle)]);\n }\n ring.push(ring[0]);\n return { type: 'Polygon', coordinates: [ring] };\n};\n\nconst normalizeGeometry = (input: SearchGeometryInput): (Polygon | MultiPolygon)[] => {\n switch (input.type) {\n case 'Polygon':\n case 'MultiPolygon':\n return [input];\n case 'Circle':\n return [circleToPolygon(input)];\n case 'FeatureCollection':\n return input.features.map((feature) => feature.geometry);\n }\n};\n\n// The places-api validates Polygon/MultiPolygon rings strictly via OpenSearch and rejects:\n// - unclosed rings → \"invalid LinearRing found (coordinates are not closed)\"\n// - consecutive duplicate points → \"invalid_shape_exception: Provided shape has duplicate consecutive coordinates\"\n// Upstream sources (reachable-area outputs, isoline libraries, hand-authored polygons) regularly\n// produce either, so we normalise here — the wire request is the one place every caller converges.\nconst dedupeConsecutive = (ring: Position[]): Position[] =>\n ring.filter((point, index) => index === 0 || point[0] !== ring[index - 1][0] || point[1] !== ring[index - 1][1]);\n\nconst closeRing = (ring: Position[]): Position[] => {\n if (ring.length < 2) return ring;\n const [first, last] = [ring[0], ring[ring.length - 1]];\n return first[0] === last[0] && first[1] === last[1] ? ring : [...ring, [...first]];\n};\n\nconst sanitizeRing = (ring: Position[]): Position[] => closeRing(dedupeConsecutive(ring));\n\nconst sanitizeGeometry = <G extends Polygon | MultiPolygon>(geometry: G): G => {\n if (geometry.type === 'Polygon') {\n return { ...geometry, coordinates: geometry.coordinates.map(sanitizeRing) } as G;\n }\n return {\n ...geometry,\n coordinates: geometry.coordinates.map((polygon) => polygon.map(sanitizeRing)),\n } as G;\n};\n\nconst toGeometriesPayload = (params: ExplorationSearchParams): (Polygon | MultiPolygon)[] | undefined => {\n if (!params.geometries?.length) return undefined;\n const geometries = params.geometries.flatMap(normalizeGeometry).map(sanitizeGeometry);\n return geometries.length ? geometries : undefined;\n};\n\nconst toMunicipalitiesPayload = (params: ExplorationSearchParams): string[] | undefined =>\n params.municipalities?.length ? params.municipalities : undefined;\n\n/**\n * Default function for building an exploration search request from {@link ExplorationSearchParams}.\n * @param params The exploration search parameters, with global configuration already merged into them.\n */\nexport const buildExplorationSearchRequest = (params: ExplorationSearchParams): ExplorationSearchRequestAPI => {\n const url = new URL('/places', buildUrlBasePath(params));\n const near = toNearPayload(params);\n const categories = toCategoriesPayload(params);\n const bboxes = toBboxesPayload(params);\n const geometries = toGeometriesPayload(params);\n const municipalities = toMunicipalitiesPayload(params);\n const data: ExplorationSearchPayloadAPI = {\n ...(params.query && { q: params.query }),\n ...(params.countries?.[0] && { country: params.countries[0] }),\n ...(municipalities && { municipalities }),\n ...(params.poiBrands?.[0] && { brand: params.poiBrands[0] }),\n ...(categories && { categories }),\n ...(params.placeTypes?.length && { types: params.placeTypes }),\n ...(params.areaId && { area_id: params.areaId }),\n ...(params.areaTags?.length && { area_tags: params.areaTags }),\n ...(near && { near }),\n ...(bboxes && { bboxes }),\n ...(geometries && { geometries }),\n ...(params.offset !== undefined && { from: params.offset }),\n ...(params.limit !== undefined && { size: params.limit }),\n };\n return { url, data };\n};\n","import type { BBox, Place, PlaceType, POI, POICategory } from '@tomtom-org/maps-sdk/core';\nimport {\n bboxFromGeoJSON,\n bboxOnlyIfWithArea,\n getPosition,\n poiIDsToCategories,\n toPointGeometry,\n} from '@tomtom-org/maps-sdk/core';\nimport type { SearchSummary } from '../shared';\nimport type {\n ExplorationPlaceProps,\n ExplorationPOIAPI,\n ExplorationRecordType,\n ExplorationSearchParams,\n ExplorationSearchResponse,\n ExplorationSearchResponseAPI,\n ExplorationSearchResultAPI,\n ExplorationViewportAPI,\n} from './types';\n\n// The places-api `types` vocabulary uses `PointAddress` (no space) while the\n// SDK's shared PlaceType uses `'Point Address'` — map on the way out so\n// consumers get the same discriminator values as the other search services.\nconst API_TYPE_TO_PLACE_TYPE: Record<ExplorationRecordType, PlaceType> = {\n POI: 'POI',\n PointAddress: 'Point Address',\n Street: 'Street',\n};\n\nconst toBBoxFromViewport = (viewport: ExplorationViewportAPI | undefined): BBox | undefined => {\n if (!viewport) return undefined;\n const [[minLon, minLat], [maxLon, maxLat]] = viewport.coordinates;\n return [minLon, minLat, maxLon, maxLat];\n};\n\n// Resolve the API's category-ID fields that describe THIS place into a deduplicated POICategory[].\n// The places-api splits categories across `categorySet` (the broad taxonomy) and `preferredCategoryId`\n// (the most specific leaf for this place); we merge them so consumers see RESTAURANT *and*\n// ITALIAN_RESTAURANT, ordered with the most specific (largest) IDs first.\n//\n// `nearbyCategoryIds` is intentionally excluded — despite the suggestive name it lists categories\n// of OTHER places near this one (a \"see also\" hint), not subcategories of this place. Including\n// it pollutes downstream rendering: PlacesModule picks `categories[0]` for the marker icon, so\n// e.g. an EV charging station near a restaurant would be rendered with a fork-and-knife icon.\nconst collectCategoryIds = (poiAPI: ExplorationPOIAPI): number[] => {\n const ids = new Set<number>();\n for (const code of poiAPI.categorySet ?? []) {\n ids.add(Number(code));\n }\n if (poiAPI.preferredCategoryId) {\n ids.add(Number(poiAPI.preferredCategoryId));\n }\n return [...ids].filter((id) => Number.isFinite(id));\n};\n\nconst collectCategories = (poiAPI: ExplorationPOIAPI): POICategory[] => {\n const ids = collectCategoryIds(poiAPI);\n // Sort descending so leaf-level categories (larger numeric IDs in the TomTom taxonomy) come\n // first — useful for callers that pick `categories[0]` as a primary label.\n ids.sort((a, b) => b - a);\n const seen = new Set<POICategory>();\n const result: POICategory[] = [];\n for (const id of ids) {\n const category = poiIDsToCategories[id];\n if (category && !seen.has(category)) {\n seen.add(category);\n result.push(category);\n }\n }\n return result;\n};\n\n// POI fields live under `hit.poi` — pick and shape into the SDK's POI type.\nconst parsePoi = (poiAPI: ExplorationPOIAPI | undefined): POI | undefined => {\n if (!poiAPI?.name) return undefined;\n return {\n name: poiAPI.name,\n categories: collectCategories(poiAPI),\n localizedCategories: poiAPI.classifications?.map((classification) => classification.name) ?? [],\n ...(poiAPI.phone && { phone: poiAPI.phone }),\n ...(poiAPI.url && { url: poiAPI.url }),\n ...(poiAPI.brands?.length && { brands: poiAPI.brands.map((brand) => brand.name) }),\n ...(poiAPI.timeZone?.ianaId && { timeZone: poiAPI.timeZone }),\n };\n};\n\nconst parseResult = (result: ExplorationSearchResultAPI): Place<ExplorationPlaceProps> => {\n const { id, position, address, score, distance_m, type, viewport } = result;\n const placeType = API_TYPE_TO_PLACE_TYPE[type ?? 'POI'];\n const poi = parsePoi(result.poi);\n const bbox = toBBoxFromViewport(viewport);\n return {\n type: 'Feature',\n id,\n geometry: toPointGeometry(position),\n ...(bbox && { bbox }),\n properties: {\n type: placeType,\n ...(address && { address }),\n ...(poi && { poi }),\n ...(score !== undefined && { score }),\n ...(distance_m !== undefined && { distance: distance_m }),\n ...(result.area_id && { areaId: result.area_id }),\n ...(result.area_country && { areaCountry: result.area_country }),\n ...(result.area_tags?.length && { areaTags: result.area_tags }),\n } as ExplorationPlaceProps,\n };\n};\n\nconst buildSummary = (apiResponse: ExplorationSearchResponseAPI, params: ExplorationSearchParams): SearchSummary => {\n const hasNear = !!getPosition(params.position);\n const geoBias = hasNear ? getPosition(params.position) : null;\n return {\n query: params.query ?? '',\n queryType: hasNear ? 'NEARBY' : 'NON_NEAR',\n queryTime: 0,\n numResults: apiResponse.hits.length,\n offset: params.offset ?? 0,\n totalResults: apiResponse.total,\n fuzzyLevel: 0,\n ...(geoBias && { geoBias }),\n };\n};\n\n/**\n * Default function to parse an exploration search response.\n * @param apiResponse The API response.\n * @param params The original request parameters.\n */\nexport const parseExplorationSearchResponse = (\n apiResponse: ExplorationSearchResponseAPI,\n params: ExplorationSearchParams,\n): ExplorationSearchResponse => {\n const features = apiResponse.hits.map(parseResult);\n const bbox = bboxOnlyIfWithArea(bboxFromGeoJSON(features));\n return {\n type: 'FeatureCollection',\n properties: buildSummary(apiResponse, params),\n features,\n ...(bbox && { bbox }),\n };\n};\n","import type { ServiceTemplate } from '../shared';\nimport { post } from '../shared/fetch';\nimport { explorationSearchRequestValidationConfig } from './explorationSearchRequestSchema';\nimport { buildExplorationSearchRequest } from './requestBuilder';\nimport { parseExplorationSearchResponse } from './responseParser';\nimport type {\n ExplorationSearchParams,\n ExplorationSearchRequestAPI,\n ExplorationSearchResponse,\n ExplorationSearchResponseAPI,\n} from './types';\n\n/**\n * Exploration search service template type.\n * @ignore\n */\nexport type ExplorationSearchTemplate = ServiceTemplate<\n ExplorationSearchParams,\n ExplorationSearchRequestAPI,\n ExplorationSearchResponseAPI,\n ExplorationSearchResponse\n>;\n\n/**\n * Exploration search service template main implementation.\n * @ignore\n */\nexport const explorationSearchTemplate: ExplorationSearchTemplate = {\n requestValidation: explorationSearchRequestValidationConfig,\n buildRequest: buildExplorationSearchRequest,\n sendRequest: post,\n parseResponse: parseExplorationSearchResponse,\n};\n","import { callService } from '../shared/serviceTemplate';\nimport type { ExplorationSearchTemplate } from './explorationSearchTemplate';\nimport { explorationSearchTemplate } from './explorationSearchTemplate';\nimport type { ExplorationSearchParams, ExplorationSearchResponse } from './types';\n\n/**\n * Search for places via the exploration places API.\n *\n * The exploration search service exposes the same input and output surface as\n * the existing {@link search} service, but targets the exploration places API\n * (POST `/places` with a JSON body) instead of the TomTom Search API.\n *\n * @remarks\n * Key features:\n * - **Fuzzy name match** via {@link ExplorationSearchParams.query}\n * - **Near filter** via `position` + `radiusMeters` (mapped to `near.coordinates` + `near.radius_km`)\n * - **Bounding box** via `boundingBox` (mapped to `bboxes`)\n * - **Geometries** via {@link ExplorationSearchParams.geometries} (Polygon,\n * MultiPolygon, Circle, or FeatureCollection — same shape as {@link geometrySearch})\n * - **Brand / category / country / municipalities** filters\n * - **Pagination** via `offset` / `limit` (mapped to `from` / `size`)\n *\n * @param params Exploration search parameters\n * @param customTemplate Advanced customization for request/response handling\n *\n * @returns Promise resolving to a collection of matching places, with the same\n * {@link SearchPlaceProps} shape used by the existing search service.\n *\n * @example\n * ```typescript\n * // Nearest restaurants to Dam Square\n * const results = await explorationSearch({\n * position: [4.9003, 52.3791],\n * radiusMeters: 5000,\n * poiCategories: ['RESTAURANT'],\n * limit: 10,\n * });\n *\n * // Inside a GeoJSON polygon\n * const inArea = await explorationSearch({\n * geometries: [{\n * type: 'Polygon',\n * coordinates: [[\n * [4.878, 52.364], [4.922, 52.364],\n * [4.922, 52.394], [4.878, 52.394],\n * [4.878, 52.364],\n * ]],\n * }],\n * poiCategories: ['RESTAURANT'],\n * });\n * ```\n *\n * @ignore\n * @experimental\n */\nexport const explorationSearch = async (\n params: ExplorationSearchParams,\n customTemplate?: Partial<ExplorationSearchTemplate>,\n): Promise<ExplorationSearchResponse> =>\n callService(params, { ...explorationSearchTemplate, ...customTemplate }, 'ExplorationSearch');\n","/**\n * Canonical area-tag vocabulary used by the places-api. Tokens are\n * lowercase `snake_case` and describe the character of the municipality\n * polygon a hit sits in (currently populated for `DE` / `NL` / `FR`).\n *\n * Two groups are exposed below:\n *\n * - **Populated tokens** — the 46 tokens the current pipeline run actually\n * emits, grouped by classification axis (settlement scale, density,\n * terrain, tourism, etc.). These are what every live `area_tags` value\n * will be drawn from today.\n * - **Reserved tokens** — vocabulary tokens that exist canonically but\n * are not populated by the current pipeline run. They may appear in\n * future runs without an API change, so callers should treat them as\n * valid even though no hit returns them today.\n *\n * The schema doc warns the vocabulary may shift between pipeline\n * iterations, so SDK consumers should code defensively. The\n * {@link AreaTag} alias falls back to `string` to keep arbitrary\n * (yet-to-be-added) tokens type-compatible without losing IDE\n * autocompletion on the known set.\n *\n * @ignore\n * @experimental\n */\nexport const POPULATED_AREA_TAGS = [\n // Settlement scale\n 'village',\n 'small_town',\n 'mid_town',\n 'suburban',\n // Density\n 'rural',\n 'semi_rural',\n 'residential',\n // Setting (terrain)\n 'inland',\n 'coastal',\n 'atlantic_coast',\n 'alpine',\n 'mountain',\n 'foothills',\n 'forest',\n 'plains',\n 'farmland',\n 'agricultural',\n // Tourism intensity\n 'no_tourism',\n 'low_tourism',\n 'moderate_tourism',\n 'tourism_economy',\n // Tourism type\n 'eco_tourism',\n 'heritage_tourism',\n 'watersports',\n // Outdoor recreation\n 'hiking',\n 'cycling',\n // Economy\n 'industrial',\n 'logistics_hub',\n 'retail_hub',\n 'shopping_district',\n 'hospital_town',\n 'dining_scene',\n // Transport\n 'car_oriented',\n 'walkable',\n 'transit_connected',\n 'transit_sparse',\n 'highway_corridor',\n 'commuter_town',\n // Traffic\n 'low_traffic',\n 'moderate_traffic',\n // Commercial character\n 'sparse_commercial',\n // Macro region\n 'western_europe',\n 'central_europe',\n 'southern_europe',\n 'north_american',\n 'iberian',\n] as const;\n\n/**\n * Vocabulary tokens that exist canonically in the area-tag taxonomy but\n * aren't emitted by the current pipeline run (per the schema doc's\n * \"tokens that exist… but are not populated\" list). Filtering on these\n * today returns zero hits, but a future pipeline run may activate them\n * without a wire-protocol change.\n *\n * @ignore\n * @experimental\n */\nexport const RESERVED_AREA_TAGS = [\n // Water / coastal sub-types\n 'lakefront',\n 'riverine',\n 'wetland',\n 'marina',\n 'harbor',\n 'beach',\n // Macro region (Mediterranean)\n 'mediterranean',\n // Destination archetypes\n 'ski_destination',\n 'beach_destination',\n 'wine_tourism',\n 'vineyard',\n 'university_town',\n 'medieval_old_town',\n 'unesco_site',\n // US sub-regions (mentioned by name in the doc)\n 'midwest_us',\n 'rust_belt',\n] as const;\n\n/**\n * Union of every populated area-tag token currently emitted by the\n * places-api pipeline.\n *\n * @ignore\n * @experimental\n */\nexport type PopulatedAreaTag = (typeof POPULATED_AREA_TAGS)[number];\n\n/**\n * Union of every canonical (populated + reserved) area-tag token. Reserved\n * tokens may begin appearing as the pipeline evolves.\n *\n * @ignore\n * @experimental\n */\nexport type CanonicalAreaTag = PopulatedAreaTag | (typeof RESERVED_AREA_TAGS)[number];\n\n/**\n * Area-tag token accepted by the places-api `area_tags` filter and\n * returned on each hit's `areaTags` property.\n *\n * Resolves to the canonical {@link CanonicalAreaTag} union for editor\n * autocompletion, but tolerates any other string so the SDK doesn't\n * hard-error on tokens added in a future pipeline run. To strictly\n * constrain inputs to the currently-known vocabulary, use\n * {@link CanonicalAreaTag} (or {@link PopulatedAreaTag}) directly.\n *\n * @example\n * ```typescript\n * // Autocomplete on the canonical vocabulary, but new tokens still type-check.\n * const tags: AreaTag[] = ['walkable', 'transit_connected'];\n * ```\n *\n * @ignore\n * @experimental\n */\nexport type AreaTag = CanonicalAreaTag | string;\n","import type { Place } from '@tomtom-org/maps-sdk/core';\nimport { callService } from '../shared/serviceTemplate';\nimport type { GeocodingTemplate } from './geocodingTemplate';\nimport { geocodingTemplate } from './geocodingTemplate';\nimport type { GeocodingParams } from './types/geocodingParams';\nimport type { GeocodingProps, GeocodingResponse } from './types/geocodingResponse';\n\n/**\n * Convert addresses into geographic coordinates (geocoding).\n *\n * The Geocode service translates addresses and place names into geographic coordinates,\n * enabling you to position markers on maps, calculate routes, or perform spatial analysis.\n *\n * @remarks\n * This service is optimized for address lookup and does not return POIs (Points of Interest).\n * For POI search, use the {@link search} function instead.\n *\n * Features:\n * - Highly tolerant of typos and incomplete addresses\n * - Handles various address formats and components\n * - Supports street addresses, intersections, and cross streets\n * - Works with higher-level geographies (cities, counties, states, countries)\n * - Returns structured address components\n *\n * @param params - Geocoding parameters including the address query\n * @param customTemplate - Advanced customization for request/response handling\n *\n * @returns Promise resolving to geocoded location results\n *\n * @example\n * ```typescript\n * // Geocode a complete address\n * const result = await geocode({\n * key: 'your-api-key',\n * query: '1600 Pennsylvania Avenue NW, Washington, DC'\n * });\n *\n * // Geocode with partial address\n * const partialResult = await geocode({\n * key: 'your-api-key',\n * query: 'Amsterdam, Netherlands'\n * });\n *\n * // Geocode with bias towards specific location\n * const biasedResult = await geocode({\n * key: 'your-api-key',\n * query: 'Main Street',\n * at: [4.9041, 52.3676], // Bias toward Amsterdam\n * limit: 5\n * });\n *\n * // Geocode an intersection\n * const intersection = await geocode({\n * key: 'your-api-key',\n * query: '5th Avenue & 42nd Street, New York'\n * });\n * ```\n *\n * @see [Geocode API Documentation](https://docs.tomtom.com/search-api/documentation/geocoding-service/geocode)\n * @see [Places Quickstart Guide](https://docs.tomtom.com/maps-sdk-js/guides/services/places/quickstart)\n * @see [Geocoding Guide](https://docs.tomtom.com/maps-sdk-js/guides/services/places/geocoding)\n *\n * @group Geocoding\n */\nexport const geocode = async (\n params: GeocodingParams,\n customTemplate?: Partial<GeocodingTemplate>,\n): Promise<GeocodingResponse> => callService(params, { ...geocodingTemplate, ...customTemplate }, 'Geocode');\n\n/**\n * Geocode a query and return the single best-matching place.\n *\n * Convenience wrapper around {@link geocode} that returns the top result directly.\n *\n * @param query - Address or place name to geocode\n * @returns Promise resolving to the best-matching place\n * @throws {Error} If no results are found for the query\n *\n * @remarks\n * Use {@link geocode} with a `limit` parameter when you need to handle the case\n * where no results are found without throwing.\n *\n * @example\n * ```typescript\n * const place = await geocodeOne('Amsterdam Centraal');\n * console.log(place.geometry.coordinates); // [lng, lat]\n * ```\n *\n * @group Geocoding\n */\nexport const geocodeOne = async (query: string): Promise<Place<GeocodingProps>> => {\n const result = await geocode({ query, limit: 1 });\n if (!result.features[0]) throw new Error(`geocodeOne: no results found for \"${query}\"`);\n return result.features[0];\n};\n","import type { CommonPlaceProps, Places, PolygonFeatures } from '@tomtom-org/maps-sdk/core';\nimport { callService } from '../shared/serviceTemplate';\nimport type { GeometryDataTemplate } from './geometryDataTemplate';\nimport { geometryDataTemplate } from './geometryDataTemplate';\nimport type { GeometryDataParams, GeometryParams, GeometryPlaceParams } from './types/geometryDataParams';\n\n/**\n * Merge our internal Places \"properties\" response with Geometry data\n * @param places\n * @param geometries\n * @returns FeatureCollection<Polygon | MultiPolygon>,\n */\nconst mergePlacesWithGeometries = (places: Places, geometries: PolygonFeatures): PolygonFeatures<CommonPlaceProps> => {\n const placesIdMap = places.features.reduce(\n (acc, place) => {\n const geometryId = place.properties.dataSources?.geometry?.id;\n\n if (geometryId) {\n acc[geometryId] = {\n ...place.properties,\n placeCoordinates: place.geometry.coordinates,\n };\n }\n return acc;\n },\n {} as Record<string, unknown>,\n );\n\n const features = geometries.features.map((feature) => {\n if (feature.id && placesIdMap[feature.id]) {\n return { ...feature, properties: placesIdMap[feature.id] };\n }\n\n return feature;\n });\n\n return {\n type: 'FeatureCollection',\n bbox: geometries.bbox,\n features,\n } as PolygonFeatures<CommonPlaceProps>;\n};\n\n/**\n * Retrieve polygon geometries representing geographic area boundaries.\n *\n * The Geometry Data service returns coordinate sets that define the outlines of\n * geographic areas such as cities, countries, administrative regions, or POI footprints.\n * These polygons enable visualization of area boundaries, spatial analysis, and\n * geofencing applications.\n *\n * @remarks\n * Key features:\n * - **Batch requests**: Fetch up to 20 geometries in a single call\n * - **Multiple scales**: From countries down to building footprints\n * - **Place integration**: Can merge with place data for enriched results\n * - **Standard GeoJSON**: Returns standard Polygon/MultiPolygon features\n *\n * Common use cases:\n * - Display city or country boundaries on maps\n * - Show POI building footprints\n * - Create geofences for spatial queries\n * - Visualize administrative divisions\n * - Calculate areas and spatial relationships\n *\n * @param params - Geometry parameters with IDs or places to fetch boundaries for\n * @param customTemplate - Advanced customization for request/response handling\n *\n * @returns Promise resolving to polygon features representing area boundaries\n *\n * @example\n * ```typescript\n * // Fetch geometry by ID\n * const cityBoundary = await geometryData({\n * key: 'your-api-key',\n * geometries: ['geometry-id-123']\n * });\n *\n * // Fetch multiple geometries at once\n * const boundaries = await geometryData({\n * key: 'your-api-key',\n * geometries: ['country-id-1', 'city-id-2', 'poi-id-3']\n * });\n *\n * // Fetch and merge with place data\n * const searchResults = await search({ query: 'Amsterdam' });\n * const withBoundaries = await geometryData({\n * key: 'your-api-key',\n * geometries: searchResults // Places with geometry IDs\n * });\n * // Result includes both place properties and polygon boundaries\n * ```\n *\n * @see [Geometry Data API Documentation](https://docs.tomtom.com/search-api/documentation/additional-data-service/additional-data)\n * @see [Places Quickstart Guide](https://docs.tomtom.com/maps-sdk-js/guides/services/places/quickstart)\n * @see [Geometry Data Guide](https://docs.tomtom.com/maps-sdk-js/guides/services/places/geometry-data)\n *\n * @group Geometry\n */\nexport async function geometryData(\n params: GeometryDataParams,\n customTemplate?: Partial<GeometryDataTemplate>,\n): Promise<PolygonFeatures>;\nexport async function geometryData(\n params: GeometryPlaceParams,\n customTemplate?: Partial<GeometryDataTemplate>,\n): Promise<PolygonFeatures<CommonPlaceProps>>;\nexport async function geometryData(params: GeometryParams, customTemplate?: Partial<GeometryDataTemplate>) {\n const geometryResult = await callService(params, { ...geometryDataTemplate, ...customTemplate }, 'GeometryData');\n\n // If params.geometries is a FeatureCollection(Place), the properties will be merged with geometry results.\n if (!Array.isArray(params.geometries) && params.geometries.type === 'FeatureCollection') {\n return mergePlacesWithGeometries(params.geometries, geometryResult);\n }\n\n return geometryResult;\n}\n","import { callService } from '../shared/serviceTemplate';\nimport type { PlaceByIdTemplate } from './placeByIdTemplate';\nimport { placeByIdTemplate } from './placeByIdTemplate';\nimport type { PlaceByIdParams, PlaceByIdResponse } from './types';\n\n/**\n * Retrieve detailed information about a place using its unique identifier.\n *\n * The Place by ID service fetches comprehensive data for a specific place when you\n * have its ID from a previous search or from a place's dataSources. This is useful\n * for getting additional details or refreshing information about a known location.\n *\n * @remarks\n * Use cases:\n * - **Fetch POI details**: Get extended information like reviews, photos, amenities\n * - **Refresh place data**: Update information for a cached place\n * - **Deep linking**: Allow users to share/bookmark specific places\n * - **Related POI navigation**: Explore parent/child relationships\n *\n * The ID can be obtained from:\n * - Previous search results (place.id)\n * - POI details data source (place.properties.dataSources.poiDetails.id)\n * - Related POIs (place.properties.relatedPois[].id)\n * - Deep links or bookmarks\n *\n * @param params - Place by ID parameters with the place identifier\n * @param customTemplate - Advanced customization for request/response handling\n *\n * @returns Promise resolving to detailed place information\n *\n * @example\n * ```typescript\n * // Get place by ID from search result\n * const searchResult = await search({ query: 'Eiffel Tower' });\n * const placeId = searchResult.features[0].id;\n *\n * const placeDetails = await placeById({\n * key: 'your-api-key',\n * entityId: placeId\n * });\n *\n * // Get extended POI details\n * const place = searchResult.features[0];\n * const poiDetailsId = place.properties.dataSources?.poiDetails?.id;\n *\n * if (poiDetailsId) {\n * const detailedPOI = await placeById({\n * key: 'your-api-key',\n * entityId: poiDetailsId\n * });\n * // May include additional photos, reviews, extended hours, etc.\n * }\n *\n * // Navigate to related POI\n * const relatedPOI = place.properties.relatedPois?.[0];\n * if (relatedPOI) {\n * const parentPlace = await placeById({\n * key: 'your-api-key',\n * entityId: relatedPOI.id\n * });\n * console.log('Parent location:', parentPlace.properties.address);\n * }\n * ```\n *\n * @see [Place by ID API Documentation](https://docs.tomtom.com/search-api/documentation/place-by-id-service/place-by-id)\n * @see [Places Quickstart Guide](https://docs.tomtom.com/maps-sdk-js/guides/services/places/quickstart)\n *\n * @group Place\n */\nexport const placeById = async (\n params: PlaceByIdParams,\n customTemplate?: Partial<PlaceByIdTemplate>,\n): Promise<PlaceByIdResponse> => callService(params, { ...placeByIdTemplate, ...customTemplate }, 'PlaceById');\n","import type { Language } from '@tomtom-org/maps-sdk/core';\nimport type { POICategoryResult } from './types';\n\nconst cache = new Map<Language, POICategoryResult[]>();\nconst textCache = new Map<Language, Record<string, POICategoryResult>>();\n\nconst DEFAULT_LANGUAGE: Language = 'en-GB';\n\nconst getWithDefault = (language: Language | undefined): Language => language ?? DEFAULT_LANGUAGE;\n\nexport const normalizeText = (text: string): string => text.toLowerCase().replaceAll(/[\\s\\-_,]+/g, '');\n\n/** @ignore */\nexport const getCachedCategories = (language: Language | undefined): POICategoryResult[] | undefined => {\n return cache.get(getWithDefault(language));\n};\n\n/** @ignore */\nexport const setCachedCategories = (language: Language | undefined, categories: POICategoryResult[]): void => {\n const lang = getWithDefault(language);\n cache.set(lang, categories);\n textCache.set(lang, buildTextCache(categories));\n};\n\n/** @ignore */\nexport const buildTextCache = (categories: POICategoryResult[]): Record<string, POICategoryResult> => {\n const cache: Record<string, POICategoryResult> = {};\n for (const category of categories) {\n const texts = [category.name, ...category.synonyms].map(normalizeText);\n for (const text of texts) {\n cache[text] = category;\n }\n }\n return cache;\n};\n\n/** @ignore */\nexport const getCachedTextEntries = (language: Language | undefined): Record<string, POICategoryResult> =>\n textCache.get(language ?? DEFAULT_LANGUAGE) ?? {};\n","import { z } from 'zod';\nimport { commonServiceRequestSchema } from '../shared/schema/commonParamsSchema';\n\n/**\n * @ignore\n */\nexport const poiCategoriesRequestSchema = commonServiceRequestSchema.extend({\n filters: z.array(z.string()).optional(),\n});\n","import { PLACES_URL_PATH } from '../shared/request/commonSearchRequestBuilder';\nimport { appendCommonParams } from '../shared/request/requestBuildingUtils';\nimport type { POICategoriesParams } from './types';\n\n/**\n * Builds the POI categories request URL from the merged params.\n * The `filters` param is intentionally omitted — it is applied client-side.\n * @ignore\n */\nexport const buildPoiCategoriesRequest = (params: POICategoriesParams): URL => {\n const baseUrl = params.customServiceBaseURL ?? `${params.commonBaseURL}${PLACES_URL_PATH}/poiCategories.json`;\n const url = new URL(baseUrl);\n appendCommonParams(url.searchParams, params);\n return url;\n};\n","import { type POICategory, poiIDsToCategories } from '@tomtom-org/maps-sdk/core';\nimport type { POICategoriesResponse, POICategoryResult, PoiCategoriesResponseAPI } from './types';\n\nconst parsePoiCategory = (api: PoiCategoriesResponseAPI['poiCategories'][number]): POICategoryResult | null => {\n const code = poiIDsToCategories[api.id];\n if (!code) {\n console.error(\n `Received unknown POI category ID ${api.id} (${api.name}). This category will be skipped and unavailable for filtering.`,\n );\n return null;\n }\n return {\n code,\n name: api.name,\n childCategoryCodes: api.childCategoryIds\n .map((id) => poiIDsToCategories[id])\n .filter((c): c is POICategory => !!c),\n synonyms: api.synonyms,\n };\n};\n\n/**\n * @ignore\n */\nexport const parsePoiCategoriesResponse = (apiResponse: PoiCategoriesResponseAPI): POICategoriesResponse => ({\n poiCategories: apiResponse.poiCategories.map(parsePoiCategory).filter((c) => c !== null),\n});\n","import type { ServiceTemplate } from '../shared';\nimport { get } from '../shared/fetch';\nimport { poiCategoriesRequestSchema } from './poiCategoriesRequestSchema';\nimport { buildPoiCategoriesRequest } from './requestBuilder';\nimport { parsePoiCategoriesResponse } from './responseParser';\nimport type { POICategoriesParams, POICategoriesResponse, PoiCategoriesResponseAPI } from './types';\n\n/**\n * @ignore\n */\nexport type PoiCategoriesTemplate = ServiceTemplate<\n POICategoriesParams,\n URL,\n PoiCategoriesResponseAPI,\n POICategoriesResponse\n>;\n\n/**\n * @ignore\n */\nexport const poiCategoriesTemplate: PoiCategoriesTemplate = {\n requestValidation: { schema: poiCategoriesRequestSchema },\n buildRequest: buildPoiCategoriesRequest,\n getAPIVersion: () => 1,\n sendRequest: get,\n parseResponse: parsePoiCategoriesResponse,\n};\n","import type { Language, POICategory } from '@tomtom-org/maps-sdk/core';\nimport { mergeFromGlobal } from '@tomtom-org/maps-sdk/core';\nimport { callService } from '../shared/serviceTemplate';\nimport { getCachedCategories, getCachedTextEntries, normalizeText, setCachedCategories } from './cache';\nimport { poiCategoriesTemplate } from './poiCategoriesTemplate';\nimport type { POICategoriesParams, POICategoriesResponse, POICategoryResult } from './types';\n\n/**\n * Filters cached categories for `language` against `filters` keyword strings.\n * Each filter is normalized and matched as a substring of category names and synonyms.\n * Results are merged, deduplicated by code, and pruned of child categories whose parent also matched.\n *\n * @param language - Language key for the cache lookup.\n * @param filters - Keyword strings to match against names and synonyms.\n */\nconst filterCategories = (language: Language | undefined, filters: string[]): POICategoryResult[] => {\n const cachedTextEntries = getCachedTextEntries(language);\n const resultsByCode = new Map<POICategory, POICategoryResult>();\n for (const filterTerm of filters) {\n const normalizedFilterInput = normalizeText(filterTerm);\n for (const [text, category] of Object.entries(cachedTextEntries)) {\n // if the category text includes the filter input, add the category to the results:\n if (text.includes(normalizedFilterInput) && category) {\n resultsByCode.set(category.code, category);\n }\n }\n }\n\n // Remove child categories whose parent also matched — the parent already implies them.\n for (const category of resultsByCode.values()) {\n for (const childCode of category.childCategoryCodes) {\n resultsByCode.delete(childCode);\n }\n }\n return [...resultsByCode.values()];\n};\n\n/**\n * Retrieve the list of POI category types supported by the TomTom Search API.\n *\n * The returned `code` values (`POICategory`) serve two purposes:\n * - Pass to `search({ poiCategories })` to filter search results by category.\n * - Pass to `POIsModule.filterCategories()` to filter the map's built-in POI icons.\n * Generic parent codes (e.g. `'RESTAURANT'`) are supported there; overly specific child\n * codes (e.g. `'ITALIAN_RESTAURANT'`) are not — use a parent or a `POICategoryGroup` instead.\n *\n * Results are cached in memory per language. The first call fetches from the API;\n * all subsequent calls — including filtered ones — are served from the cache.\n *\n * @param params - Optional parameters. Uses global `TomTomConfig` by default.\n *\n * @example\n * ```typescript\n * // All categories\n * const { poiCategories: all } = await getPOICategories();\n *\n * // Filtered by keyword, merged and deduplicated\n * const { poiCategories: gyms } = await getPOICategories({ filters: ['gym'] });\n * gyms.forEach(c => console.log(c.code, c.name, c.childCategoryCodes));\n * ```\n *\n * @group POI Categories\n */\nexport const getPOICategories = async (params: POICategoriesParams = {}): Promise<POICategoriesResponse> => {\n const { language } = mergeFromGlobal(params);\n\n let allCategories = getCachedCategories(language);\n\n if (!allCategories) {\n const response = await callService(params, poiCategoriesTemplate, 'POICategories');\n allCategories = response.poiCategories;\n setCachedCategories(language, allCategories);\n }\n\n if (params.filters?.length) {\n return { poiCategories: filterCategories(language, params.filters) };\n }\n\n return { poiCategories: allCategories };\n};\n\n/**\n * Convenience wrapper around {@link getPOICategories} that returns a flat array of `POICategory` codes.\n *\n * The returned codes serve two purposes:\n * - Pass to `search({ poiCategories })` to filter search results by category.\n * - Pass to `POIsModule.filterCategories()` to filter the map's built-in POI icons.\n * Generic parent codes (e.g. `'RESTAURANT'`) are supported there; overly specific child\n * codes (e.g. `'ITALIAN_RESTAURANT'`) are not — use a parent or a `POICategoryGroup` instead.\n *\n * Accepts the same parameters as {@link getPOICategories} — use `filters` to narrow by keyword.\n *\n * @param params - Optional parameters. Uses global `TomTomConfig` by default.\n *\n * @example\n * ```typescript\n * // All category codes\n * const codes = await getPOICategoryCodes();\n *\n * // Codes matching 'restaurant' — for search or generic map POI filtering\n * const restaurantCodes = await getPOICategoryCodes({ filters: ['restaurant'] });\n * ```\n *\n * @group POI Categories\n */\nexport const getPOICategoryCodes = async (params: POICategoriesParams = {}): Promise<POICategory[]> => {\n const { poiCategories: categories } = await getPOICategories(params);\n return categories.map((category) => category.code);\n};\n","import { bboxFromGeoJSON, PolygonFeature, PolygonFeatures } from '@tomtom-org/maps-sdk/core';\nimport { SDKServiceError } from '../shared/errors';\nimport { callService } from '../shared/serviceTemplate';\nimport type { ReachableRangeTemplate } from './reachableRangeTemplate';\nimport { reachableRangeTemplate } from './reachableRangeTemplate';\nimport type { ReachableRangeParams } from './types/reachableRangeParams';\n\n/**\n * Calculate the area reachable from a starting point within given constraints.\n *\n * The Reachable Range service (also known as isochrone) computes a polygon representing\n * all locations that can be reached from an origin within specified limits of time, distance,\n * or energy consumption. This is essential for range analysis, service area visualization,\n * and logistics planning.\n *\n * @remarks\n * Key features:\n * - **Time-based ranges**: Areas reachable within a time budget (e.g., 30 minutes)\n * - **Distance-based ranges**: Areas within a distance limit (e.g., 50km)\n * - **Energy-based ranges**: EV range considering battery consumption\n * - **Traffic awareness**: Accounts for current and historic traffic patterns\n * - **Vehicle-specific**: Considers vehicle characteristics and constraints\n *\n * Common use cases:\n * - **Service area mapping**: \"Show areas we can deliver to within 1 hour\"\n * - **EV range visualization**: Display drivable range on current battery\n * - **Real estate**: \"Find homes within 45 minutes of workplace\"\n * - **Emergency services**: Calculate ambulance/fire truck response areas\n * - **Market analysis**: Identify customer catchment areas\n * - **Location planning**: Optimize facility placement based on coverage\n *\n * @param params Reachable range parameters including origin and budget constraints\n * @param customTemplate Advanced customization for request/response handling\n *\n * @returns Promise resolving to a polygon representing the reachable area\n *\n * @example\n * ```typescript\n * // Calculate 30-minute drive time range\n * const range30min = await calculateReachableRange({\n * key: 'your-api-key',\n * origin: [4.9041, 52.3676], // Amsterdam\n * timeBudgetInSec: 1800 // 30 minutes\n * });\n *\n * // Calculate 50km distance range\n * const range50km = await calculateReachableRange({\n * key: 'your-api-key',\n * origin: [4.9041, 52.3676],\n * distanceBudgetInMeters: 50000\n * });\n *\n * // EV range based on battery\n * const evRange = await calculateReachableRange({\n * key: 'your-api-key',\n * origin: [4.9041, 52.3676],\n * fuelBudgetInkWh: 20, // Remaining battery\n * vehicleEngineType: 'electric',\n * constantSpeedConsumptionInkWhPerHundredkm: [[50, 8], [80, 12]]\n * });\n *\n * // Traffic-aware range at departure time\n * const morningRange = await calculateReachableRange({\n * key: 'your-api-key',\n * origin: [4.9041, 52.3676],\n * timeBudgetInSec: 2700, // 45 minutes\n * departAt: new Date('2025-10-20T08:00:00Z') // Rush hour\n * });\n * ```\n *\n * @see [Reachable Range API Documentation](https://docs.tomtom.com/routing-api/documentation/routing/calculate-reachable-range)\n * @see [Routing Quickstart Guide](https://docs.tomtom.com/maps-sdk-js/guides/services/routing/quickstart)\n *\n * @group Routing\n */\nexport const calculateReachableRange = async (\n params: ReachableRangeParams,\n customTemplate?: Partial<ReachableRangeTemplate>,\n): Promise<PolygonFeature<ReachableRangeParams>> =>\n callService(params, { ...reachableRangeTemplate, ...customTemplate }, 'Reachable Range');\n\n/**\n * Calculate multiple reachable range areas from different origins or with different constraints.\n *\n * Computes several isochrone polygons in sequence, useful for comparing ranges from multiple\n * locations, visualizing multiple time/distance rings from the same origin, or analyzing\n * different vehicle scenarios.\n *\n * @remarks\n * Use cases:\n * - **Multi-location comparison**: Compare service areas of different store locations\n * - **Concentric ranges**: Create 15/30/45 minute drive time rings from one origin\n * - **Scenario analysis**: Compare ranges for different vehicle types or times of day\n * - **Coverage optimization**: Find optimal facility locations with minimal overlap\n *\n * Note: Ranges are calculated sequentially to avoid rate limiting. For large batches,\n * consider implementing your own parallel processing with appropriate throttling.\n *\n * @param paramsArray Array of reachable range parameters, one for each area to calculate\n * @param options.signal An `AbortSignal` to cancel in-flight requests between iterations.\n * @param customTemplate Advanced customization for request/response handling\n *\n * @returns Promise resolving to a FeatureCollection of reachable area polygons\n *\n * @example\n * ```typescript\n * // Calculate concentric drive time rings (15, 30, 45 minutes)\n * const timeRings = await calculateReachableRanges([\n * { key: 'your-api-key', origin: [4.9, 52.3], timeBudgetInSec: 900 },\n * { key: 'your-api-key', origin: [4.9, 52.3], timeBudgetInSec: 1800 },\n * { key: 'your-api-key', origin: [4.9, 52.3], timeBudgetInSec: 2700 }\n * ]);\n *\n * // Compare service areas of multiple stores\n * const storeRanges = await calculateReachableRanges([\n * { key: 'your-api-key', origin: [4.9, 52.3], timeBudgetInSec: 1800 },\n * { key: 'your-api-key', origin: [4.5, 51.9], timeBudgetInSec: 1800 },\n * { key: 'your-api-key', origin: [5.1, 52.1], timeBudgetInSec: 1800 }\n * ]);\n *\n * // Compare EV ranges at different battery levels\n * const batteryRanges = await calculateReachableRanges([\n * {\n * key: 'your-api-key',\n * origin: [4.9, 52.3],\n * fuelBudgetInkWh: 10,\n * vehicleEngineType: 'electric'\n * },\n * {\n * key: 'your-api-key',\n * origin: [4.9, 52.3],\n * fuelBudgetInkWh: 20,\n * vehicleEngineType: 'electric'\n * }\n * ]);\n * ```\n *\n * @see [Reachable Range API Documentation](https://docs.tomtom.com/routing-api/documentation/routing/calculate-reachable-range)\n * @see [Routing Quickstart Guide](https://docs.tomtom.com/maps-sdk-js/guides/services/routing/quickstart)\n *\n * @group Routing\n */\nexport const calculateReachableRanges = async (\n paramsArray: ReachableRangeParams[],\n options?: { signal?: AbortSignal },\n customTemplate?: Partial<ReachableRangeTemplate>,\n): Promise<PolygonFeatures<ReachableRangeParams>> => {\n const features: PolygonFeature<ReachableRangeParams>[] = [];\n for (const params of paramsArray) {\n // Throws AbortError if a newer call has cancelled this one before the next request starts\n options?.signal?.throwIfAborted();\n try {\n // we sequentially fetch reachable ranges (less speed but better to prevent QPS limit breaches):\n features.push(await calculateReachableRange(params, customTemplate));\n } catch (error) {\n // Re-throw non-API errors (e.g. validation/programming errors) and critical HTTP errors\n // (403 Forbidden, 429 Too Many Requests) — only silently skip API errors that indicate\n // no reachable range could be computed at this location (e.g. no road network).\n if (!(error instanceof SDKServiceError) || error.status === 403 || error.status === 429) {\n throw error;\n }\n }\n }\n const bbox = bboxFromGeoJSON(features);\n return { type: 'FeatureCollection', ...(bbox && { bbox }), features };\n};\n","import type { Place, RevGeoAddressProps } from '@tomtom-org/maps-sdk/core';\nimport { callService } from '../shared/serviceTemplate';\nimport type { ReverseGeocodingTemplate } from './reverseGeocodingTemplate';\nimport { reverseGeocodingTemplate } from './reverseGeocodingTemplate';\nimport type { ReverseGeocodingParams } from './types/reverseGeocodingParams';\n\n/**\n * Response from the reverse geocoding service.\n *\n * Contains a place with address information for the given coordinates.\n *\n * @group Reverse Geocoding\n */\nexport type ReverseGeocodingResponse = Place<RevGeoAddressProps>;\n\n/**\n * Convert geographic coordinates into human-readable addresses (reverse geocoding).\n *\n * Reverse geocoding translates latitude/longitude coordinates into street addresses,\n * which is essential for location-based applications that need to display addresses\n * from GPS coordinates or map clicks.\n *\n * @remarks\n * Common use cases:\n * - **Tracking applications**: Convert GPS coordinates from devices into addresses\n * - **Map interactions**: Display address when user clicks on map\n * - **Location sharing**: Show readable location instead of coordinates\n * - **Delivery apps**: Confirm pickup/dropoff addresses from driver location\n * - **Asset tracking**: Display current location of vehicles or equipment\n *\n * Features:\n * - Returns complete address hierarchy (street, city, state, country)\n * - Returns the result type (address, street, or geography)\n * - Narrows results to administrative areas with `geographyType`\n * - Direction-aware street matching with `heading`\n *\n * @param params - Reverse geocoding parameters including coordinates\n * @param customTemplate - Advanced customization for request/response handling\n *\n * @returns Promise resolving to the address for the given coordinates\n *\n * @example\n * ```typescript\n * // Get address for coordinates\n * const address = await reverseGeocode({\n * key: 'your-api-key',\n * position: [4.9041, 52.3676] // Amsterdam coordinates\n * });\n * // Returns: Dam, 1012 Amsterdam, Netherlands\n *\n * // Get the address matched along a given travel direction\n * const specificAddress = await reverseGeocode({\n * key: 'your-api-key',\n * position: [-77.0369, 38.8977], // Washington DC\n * heading: 90 // Traveling east\n * });\n *\n * // Get the containing municipality instead of the street\n * const municipality = await reverseGeocode({\n * key: 'your-api-key',\n * position: [-74.0060, 40.7128], // New York\n * geographyType: ['Municipality']\n * });\n * ```\n *\n * @see [Reverse Geocode API Documentation](https://docs.tomtom.com/search-api/documentation/reverse-geocoding-service/reverse-geocode)\n * @see [Places Quickstart Guide](https://docs.tomtom.com/maps-sdk-js/guides/services/places/quickstart)\n * @see [Reverse Geocoding Guide](https://docs.tomtom.com/maps-sdk-js/guides/services/places/reverse-geocoding)\n *\n * @group Reverse Geocoding\n */\nexport const reverseGeocode = async (\n params: ReverseGeocodingParams,\n customTemplate?: Partial<ReverseGeocodingTemplate>,\n): Promise<ReverseGeocodingResponse> =>\n callService(params, { ...reverseGeocodingTemplate, ...customTemplate }, 'ReverseGeocode');\n","import type { Routes } from '@tomtom-org/maps-sdk/core';\nimport { callService } from '../shared/serviceTemplate';\nimport type { CalculateRouteTemplate } from './calculateRouteTemplate';\nimport { calculateRouteTemplate } from './calculateRouteTemplate';\nimport type { CalculateRouteParams } from './types/calculateRouteParams';\n\n/**\n * Calculates a route between an origin and destination, optionally passing through extra waypoints.\n *\n * @param params - Route calculation parameters including locations, travel mode, and other options\n * @param customTemplate - Optional template customization for request/response handling\n * @returns Promise resolving to calculated route(s) with geometry, distance, and travel time\n *\n * @see [Calculate Route API Documentation](https://docs.tomtom.com/routing-api/documentation/tomtom-maps/calculate-route)\n *\n * @group Routing\n */\nexport const calculateRoute = async (\n params: CalculateRouteParams,\n customTemplate?: Partial<CalculateRouteTemplate>,\n): Promise<Routes> => callService(params, { ...calculateRouteTemplate, ...customTemplate }, 'Routing');\n","import { z } from 'zod';\nimport { commonSearchParamsSchema } from '../search/commonSearchParamsSchema';\nimport { commonGeocodeAndFuzzySearchParamsSchema } from '../shared/schema/commonGeocodeAndFuzzySearchParamsSchema';\n\nconst fuzzySearchRequestOptional = z.object({\n minFuzzyLevel: z.number().min(1).max(4).optional(),\n maxFuzzyLevel: z.number().min(1).max(4).optional(),\n});\n\n/**\n * @ignore\n */\nexport const fuzzySearchRequestSchema = commonSearchParamsSchema.extend(\n commonGeocodeAndFuzzySearchParamsSchema.extend(fuzzySearchRequestOptional.shape).shape,\n);\n","import { bboxFromGeoJSON } from '@tomtom-org/maps-sdk/core';\nimport { arrayToCSV } from '../shared/arrays';\nimport { appendCommonSearchParams, PLACES_URL_PATH } from '../shared/request/commonSearchRequestBuilder';\nimport { appendByJoiningParamValue, appendOptionalParam } from '../shared/request/requestBuildingUtils';\nimport type { FuzzySearchParams } from './types';\n\nconst buildUrlBasePath = (params: FuzzySearchParams): string =>\n params.customServiceBaseURL ?? `${params.commonBaseURL}${PLACES_URL_PATH}/search/${params.query ?? ''}.json`;\n\n/**\n * Default function for building a fuzzy search request from {@link FuzzySearchParams}\n * @param params The fuzzy search parameters, with global configuration already merged into them.\n */\nexport const buildFuzzySearchRequest = (params: FuzzySearchParams): URL => {\n const url = new URL(`${buildUrlBasePath(params)}`);\n appendCommonSearchParams(url, params);\n const urlParams = url.searchParams;\n appendOptionalParam(urlParams, 'typeahead', params.typeahead);\n appendOptionalParam(urlParams, 'ofs', params.offset);\n appendByJoiningParamValue(urlParams, 'countrySet', params.countries);\n appendOptionalParam(urlParams, 'radius', params.radiusMeters);\n const bbox = params.boundingBox && bboxFromGeoJSON(params.boundingBox);\n if (bbox) {\n urlParams.append('topLeft', arrayToCSV([bbox[3], bbox[0]]));\n urlParams.append('btmRight', arrayToCSV([bbox[1], bbox[2]]));\n }\n appendOptionalParam(urlParams, 'minFuzzyLevel', params.minFuzzyLevel);\n appendOptionalParam(urlParams, 'maxFuzzyLevel', params.maxFuzzyLevel);\n return url;\n};\n","import { bboxFromGeoJSON, bboxOnlyIfWithArea } from '@tomtom-org/maps-sdk/core';\nimport { latLonAPIToPosition } from '../shared/geometry';\nimport { parseSearchAPIResult, parseSummaryAPI } from '../shared/searchResultParsing';\nimport type { FuzzySearchResponse, FuzzySearchResponseAPI, QueryIntent, QueryIntentAPI } from './types';\n\nconst queryIntentApiToSdk = (intentApi: QueryIntentAPI): QueryIntent => {\n let intent;\n switch (intentApi.type) {\n case 'COORDINATE':\n intent = { ...intentApi, details: { position: latLonAPIToPosition(intentApi.details) } };\n break;\n case 'NEARBY':\n intent = {\n ...intentApi,\n details: {\n position: latLonAPIToPosition({ lon: intentApi.details.lon, lat: intentApi.details.lat }),\n text: intentApi.details.text,\n query: intentApi.details.query,\n },\n };\n break;\n case 'BOOKMARK':\n case 'W3W':\n intent = intentApi;\n }\n return intent;\n};\n\n/**\n * Default function to parse a fuzzy search response.\n * @param apiResponse The API response.\n */\nexport const parseFuzzySearchResponse = (apiResponse: FuzzySearchResponseAPI): FuzzySearchResponse => {\n const features = apiResponse.results.map(parseSearchAPIResult);\n const bbox = bboxOnlyIfWithArea(bboxFromGeoJSON(features));\n return {\n type: 'FeatureCollection',\n properties: {\n ...parseSummaryAPI(apiResponse.summary),\n queryIntent: apiResponse.summary.queryIntent.map(queryIntentApiToSdk),\n },\n features,\n ...(bbox && { bbox }),\n };\n};\n","import type { ServiceTemplate } from '../shared';\nimport { get } from '../shared/fetch';\nimport { fuzzySearchRequestSchema } from './fuzzySearchRequestSchema';\nimport { buildFuzzySearchRequest } from './requestBuilder';\nimport { parseFuzzySearchResponse } from './responseParser';\nimport type { FuzzySearchParams, FuzzySearchResponse, FuzzySearchResponseAPI } from './types';\n\n/**\n * Fuzzy search service template type.\n * @ignore\n */\nexport type FuzzySearchTemplate = ServiceTemplate<FuzzySearchParams, URL, FuzzySearchResponseAPI, FuzzySearchResponse>;\n\n/**\n * Fuzzy search service template main implementation.\n * @ignore\n */\nexport const fuzzySearchTemplate: FuzzySearchTemplate = {\n requestValidation: { schema: fuzzySearchRequestSchema },\n buildRequest: buildFuzzySearchRequest,\n sendRequest: get,\n parseResponse: parseFuzzySearchResponse,\n};\n","import { callService } from '../shared/serviceTemplate';\nimport type { FuzzySearchTemplate } from './fuzzySearchTemplate';\nimport { fuzzySearchTemplate } from './fuzzySearchTemplate';\nimport type { FuzzySearchParams, FuzzySearchResponse } from './types';\n\n/**\n * Search for places using free-text queries with fuzzy matching.\n *\n * The Fuzzy Search service provides a flexible search that handles typos, abbreviations,\n * and incomplete addresses. It searches across POIs (Points of Interest), addresses,\n * and geographic areas to find the best matches for your query.\n *\n * @remarks\n * Key features:\n * - **Typo tolerance**: Handles misspellings and typing errors\n * - **Partial matching**: Works with incomplete queries\n * - **Multi-category search**: Searches POIs, addresses, and places simultaneously\n * - **Position bias**: Prioritizes results near a given location\n * - **Flexible input**: Accepts natural language queries\n *\n * The service is ideal for:\n * - User-facing search boxes where typos are common\n * - Location lookup without knowing exact names\n * - General \"find anything\" search functionality\n * - Autocomplete with final selection\n *\n * @param params Fuzzy search parameters including the search query\n * @param customTemplate Advanced customization for request/response handling\n *\n * @returns Promise resolving to a collection of matching places\n *\n * @example\n * ```typescript\n * // Basic search with typo tolerance\n * const results = await fuzzySearch({\n * query: 'amstrdam' // Typo: missing 'e'\n * });\n * // Still finds \"Amsterdam\"\n *\n * // Search near a specific location\n * const nearby = await fuzzySearch({\n * query: 'pizza',\n * position: [4.9041, 52.3676], // Amsterdam\n * radiusMeters: 2000, // Within 2km\n * limit: 10\n * });\n *\n * // Search with category filter\n * const restaurants = await fuzzySearch({\n * poiCategories: ['ITALIAN_RESTAURANT'],\n * position: [4.9041, 52.3676]\n * });\n *\n * // Partial address search restricted to a country\n * const addresses = await fuzzySearch({\n * query: '123 main st',\n * countries: ['US'],\n * limit: 5\n * });\n * ```\n *\n * @see [Fuzzy Search API Documentation](https://docs.tomtom.com/search-api/documentation/search-service/fuzzy-search)\n * @see [Places Quickstart Guide](https://docs.tomtom.com/maps-sdk-js/guides/services/places/quickstart)\n * @see [Search Guide](https://docs.tomtom.com/maps-sdk-js/guides/services/places/search)\n *\n * @ignore (exposed via 'search')\n */\nexport const fuzzySearch = async (\n params: FuzzySearchParams,\n customTemplate?: Partial<FuzzySearchTemplate>,\n): Promise<FuzzySearchResponse> => callService(params, { ...fuzzySearchTemplate, ...customTemplate }, 'FuzzySearch');\n","import type { Place, Places, SearchPlaceProps } from '@tomtom-org/maps-sdk/core';\nimport type { AlongRouteSearchParams } from '../along-route-search';\nimport { alongRouteSearch } from '../along-route-search/alongRouteSearch';\nimport type { AlongRouteSearchTemplate } from '../along-route-search/alongRouteSearchTemplate';\nimport type { FuzzySearchParams, QueryIntent } from '../fuzzy-search';\nimport { fuzzySearch } from '../fuzzy-search/fuzzySearch';\nimport type { FuzzySearchTemplate } from '../fuzzy-search/fuzzySearchTemplate';\nimport type { GeometrySearchParams } from '../geometry-search';\nimport { geometrySearch } from '../geometry-search/geometrySearch';\nimport type { GeometrySearchTemplate } from '../geometry-search/geometrySearchTemplate';\nimport type { SearchSummary } from '../shared';\n\ntype SearchFeatureCollectionProps = SearchSummary & {\n queryIntent?: QueryIntent[];\n};\n\n/**\n * Search service response containing places that match the query.\n *\n * Collection of place features with search-specific properties like relevance scores and distances.\n *\n * @group Search\n */\nexport type SearchResponse = Places<SearchPlaceProps, SearchFeatureCollectionProps>;\n\n/**\n * Universal search function for finding places by text query or within geometries.\n *\n * This is a unified interface that automatically routes to either:\n * - **Geometry Search**: When geometries parameter is provided (search within specific areas)\n * - **Fuzzy Search**: When no geometries provided (free-text search)\n *\n * @remarks\n * The search service provides:\n * - POI (Points of Interest) search\n * - Address search\n * - Geographic area search\n * - Category-based filtering\n * - Position-based relevance ranking\n *\n * Results are ranked by relevance with scores and optional distances.\n *\n * @param params - Search parameters (either GeometrySearchParams or FuzzySearchParams)\n * @param customTemplate - Advanced customization for request/response handling\n *\n * @returns Promise resolving to a collection of matching places\n *\n * @example\n * ```typescript\n * // Free-text search near a location\n * const results = await search({\n * query: 'pizza restaurant',\n * position: [4.9041, 52.3676], // Amsterdam\n * limit: 10\n * });\n *\n * // Search within a specific area\n * const areaResults = await search({\n * query: 'coffee shop',\n * geometries: [polygon], // Search within this polygon\n * limit: 20\n * });\n *\n * // Category search\n * const restaurants = await search({\n * poiCategories: ['ITALIAN_RESTAURANT'],\n * position: [4.9041, 52.3676],\n * radiusMeters: 5000 // Within 5km\n * });\n * ```\n *\n * @see [Search API Documentation](https://docs.tomtom.com/search-api/documentation/search-service/search-service)\n * @see [Places Quickstart Guide](https://docs.tomtom.com/maps-sdk-js/guides/services/places/quickstart)\n * @see [Search Guide](https://docs.tomtom.com/maps-sdk-js/guides/services/places/search)\n *\n * @group Search\n */\nexport const search = async (\n params: GeometrySearchParams | FuzzySearchParams | AlongRouteSearchParams,\n customTemplate?: Partial<GeometrySearchTemplate | FuzzySearchTemplate | AlongRouteSearchTemplate>,\n): Promise<SearchResponse> => {\n if ('route' in params) return alongRouteSearch(params, customTemplate as Partial<AlongRouteSearchTemplate>);\n if ('geometries' in params) return geometrySearch(params, customTemplate as Partial<GeometrySearchTemplate>);\n return fuzzySearch(params, customTemplate as Partial<FuzzySearchTemplate>);\n};\n\n/**\n * Search for a single place by text query.\n *\n * Convenience function that calls {@link search} and returns the first result.\n * Throws an error if no results are found.\n *\n * @param query - Search query string\n * @returns Promise resolving to the first matching place\n * @throws {Error} If no results are found for the query\n *\n * @example\n * ```typescript\n * const place = await searchOne('Vondelpark Amsterdam');\n * console.log(place.properties.poi?.name);\n * ```\n *\n * @remarks\n * * Useful to quickly find a single place, particularly a POI.\n * * If you want to find a single address, consider 'geocodeOne'.\n *\n * @group Search\n */\nexport const searchOne = async (query: string): Promise<Place<SearchPlaceProps>> => {\n const result = await search({ query, limit: 1 });\n if (!result.features[0]) throw new Error(`searchOne: no results found for \"${query}\"`);\n return result.features[0];\n};\n","import type { TrafficAreaAnalytics } from '@tomtom-org/maps-sdk/core';\nimport { callService } from '../shared/serviceTemplate';\nimport type { TrafficAreaAnalyticsTemplate } from './trafficAreaAnalyticsTemplate';\nimport { trafficAreaAnalyticsTemplate } from './trafficAreaAnalyticsTemplate';\nimport type { TrafficAreaAnalyticsParams } from './types/trafficAreaAnalyticsParams';\n\n/**\n * Run a synchronous traffic area analytics report for a polygon region.\n *\n * Submits a single Polygon region with a date range and requested metrics, and\n * receives the full analysis result immediately (no polling required).\n *\n * @remarks\n * **Lite report constraints:**\n * - Only a single region (Polygon Feature) may be submitted per request.\n * - The date range (`startDate` → `endDate`) must not exceed 31 days.\n *\n * **Key data returned per region:**\n * - `baseData`: overall aggregated metrics for the full analysis period\n * - `timedData`: time-series breakdown at yearly / monthly / weekly / daily / hourly granularity\n * - `tiledData`: per-tile heatmap metrics (when available)\n * - `anomalies`: detected traffic anomalies keyed by camelCase metric key (e.g. `anomalies.speed`)\n *\n * **Available metrics (`metrics`):**\n * - `speed` — average speed on the road network (km/h)\n * - `freeFlowSpeed` — average speed under uncongested conditions (km/h)\n * - `congestionLevel` — percentage increase in travel time above free-flow\n * - `travelTime` — average travel time per 10 km (minutes)\n * - `networkLength` — total length of road segments with data (meters)\n *\n * @param params - Traffic Area Analytics parameters\n * @param customTemplate - Advanced customization for request/response handling\n *\n * @returns Promise resolving to a {@link TrafficAreaAnalytics} FeatureCollection\n *\n * @example\n * ```typescript\n * // Basic speed and congestion analysis for Amsterdam\n * const result = await trafficAreaAnalytics({\n * startDate: new Date('2024-08-06'),\n * endDate: new Date('2024-08-06'),\n * functionalRoadClasses: ['MOTORWAY', 'MAJOR_ROAD', 'OTHER_MAJOR_ROAD', 'SECONDARY_ROAD', 'LOCAL_CONNECTING_ROAD', 'LOCAL_ROAD_HIGH_IMPORTANCE'],\n * hours: [7, 8, 9, 17, 18],\n * metrics: ['speed', 'congestionLevel'],\n * geometry: {\n * type: 'Polygon',\n * coordinates: [\n * [[4.896128, 52.382402], [4.875701, 52.368459], [4.923611, 52.36341], [4.896128, 52.382402]]\n * ]\n * }\n * });\n *\n * const region = result.features[0];\n * console.log(region.properties.baseData.speed); // avg speed (km/h)\n * console.log(region.properties.baseData.congestionLevel); // congestion %\n * console.log(region.properties.timedData.hourly); // hourly breakdown\n * ```\n *\n * @example\n * ```typescript\n * // Full metrics over a multi-day period\n * const result = await trafficAreaAnalytics({\n * startDate: new Date('2024-08-01'),\n * endDate: new Date('2024-08-07'),\n * functionalRoadClasses: 'all',\n * hours: 'all',\n * metrics: ['speed', 'freeFlowSpeed', 'congestionLevel', 'travelTime', 'networkLength'],\n * geometry: {\n * type: 'Polygon',\n * coordinates: [[[4.89, 52.37], [4.91, 52.37], [4.91, 52.39], [4.89, 52.39], [4.89, 52.37]]]\n * }\n * });\n *\n * result.features[0].properties.timedData.daily?.forEach(day => {\n * console.log(day.date, day.congestionLevel);\n * });\n * ```\n *\n * @see [Area Analytics API Documentation](https://developer.tomtom.com/area-analytics/documentation/api/analysis-lite)\n *\n * @group Traffic\n */\nexport const trafficAreaAnalytics = async (\n params: TrafficAreaAnalyticsParams,\n customTemplate?: Partial<TrafficAreaAnalyticsTemplate>,\n): Promise<TrafficAreaAnalytics> =>\n callService(params, { ...trafficAreaAnalyticsTemplate, ...customTemplate }, 'TrafficAreaAnalytics');\n","import type { TrafficIncidentDetails } from '@tomtom-org/maps-sdk/core';\nimport { callService } from '../shared/serviceTemplate';\nimport type { TrafficIncidentDetailsTemplate } from './trafficIncidentDetailsTemplate';\nimport { trafficIncidentDetailsTemplate } from './trafficIncidentDetailsTemplate';\nimport type { TrafficIncidentDetailsParams } from './types/trafficIncidentDetailsParams';\n\n/**\n * Fetch detailed information about traffic incidents.\n *\n * Query incidents either by a geographic bounding box or by a list of incident IDs.\n * Results are GeoJSON Features whose geometry is a `Point` (localised incidents) or\n * a `LineString` (incidents spanning a stretch of road).\n *\n * @remarks\n * **Two query modes:**\n *\n * - **Bounding box** (`bbox`): Returns all incidents within the given area.\n * Maximum area is 10,000 km².\n * - **By IDs** (`ids`): Returns the specified incidents directly.\n * GET is used automatically for up to 5 IDs; POST for up to 100 IDs.\n *\n * **Key data provided per incident:**\n * - Category (`iconCategory`): type of incident (accident, roadworks, jam, etc.)\n * - Delay magnitude: severity of the delay (`minor`, `moderate`, `major`, …)\n * - Geometry: exact location or extent of the incident on the road network\n * - Time information: start/end times and whether the incident is `present` or `future`\n * - Events: one or more detailed event descriptions\n *\n * @param params - Traffic Incident Details parameters (bbox or ids)\n * @param customTemplate - Advanced customization for request/response handling\n *\n * @returns Promise resolving to a {@link TrafficIncidentDetails} object containing the matching incidents\n *\n * @example\n * ```typescript\n * // Query by bounding box (Amsterdam area)\n * const result = await trafficIncidentDetails({\n * bbox: [4.728, 52.278, 5.080, 52.479]\n * });\n *\n * result.incidents.forEach(incident => {\n * console.log(incident.properties.iconCategory); // 'accident' | 'jam' | …\n * console.log(incident.properties.magnitudeOfDelay); // 'minor' | 'major' | …\n * console.log(incident.geometry); // Point or LineString\n * });\n * ```\n *\n * @example\n * ```typescript\n * // Query up to 5 specific incidents by ID (GET)\n * const result = await trafficIncidentDetails({\n * ids: ['incident-id-1', 'incident-id-2']\n * });\n * ```\n *\n * @example\n * ```typescript\n * // Query many incident IDs — POST is used automatically when ids.length > 5\n * const result = await trafficIncidentDetails({\n * ids: largeIdArray\n * });\n * ```\n *\n * @example\n * ```typescript\n * // Filter by category and include future incidents\n * const result = await trafficIncidentDetails({\n * bbox: [4.728, 52.278, 5.080, 52.479],\n * categoryFilter: ['accident', 'road-closed'],\n * timeValidityFilter: ['present', 'future']\n * });\n * ```\n *\n * @see [Incident Details API Documentation](https://docs.tomtom.com/traffic-api/documentation/tomtom-orbis-maps/traffic-incidents/incident-details)\n * @see [Incident Details Guide](https://docs.tomtom.com/maps-sdk-js/guides/services/traffic/incident-details)\n *\n * @group Traffic\n */\nexport const trafficIncidentDetails = async (\n params: TrafficIncidentDetailsParams,\n customTemplate?: Partial<TrafficIncidentDetailsTemplate>,\n): Promise<TrafficIncidentDetails> =>\n callService(params, { ...trafficIncidentDetailsTemplate, ...customTemplate }, 'TrafficIncidentDetails');\n"],"mappings":"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GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,GAAI,MACJ,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"}