@independo/leaflet-independo-maps
Version:
Leaflet plugin for displaying points of interest as pictograms.
511 lines (498 loc) • 18.1 kB
TypeScript
import L, { LatLngExpression, Map, LatLngBounds } from 'leaflet';
interface Pictogram {
/**
* A unique identifier for the pictogram.
* This ID is provided by the underlying data source (e.g., Global Symbols API).
*/
id: string;
/**
* URL of the pictogram.
*/
url: string;
/**
* Text to display below the pictogram.
*/
displayText: string;
/**
* ARIA label for the pictogram.
*/
ariaLabel?: string;
/**
* Description of the pictogram.
*/
description?: string;
/**
* A flexible container for any additional metadata from the data source.
*/
metadata?: Record<string, any>;
}
/**
* Represents a single point of interest (POI).
*
* A Point of Interest (POI) is a location that has specific significance,
* such as a restaurant, park, store, landmark, or other notable place.
* Each POI contains key information such as its name, type, and geographic location.
*/
interface PointOfInterest {
/**
* A unique identifier for the point of interest.
* This ID is provided by the underlying data source (e.g., Google Places, Overpass API).
*/
id: string;
/**
* The human-readable name of the point of interest.
* @example "Cafe Mozart", "Schönbrunn Palace"
*/
name: string;
/**
* The category or type of the point of interest.
* @example "restaurant", "park", "museum"
*/
type: string;
/**
* The latitude coordinate of the point of interest.
*/
latitude: number;
/**
* The longitude coordinate of the point of interest.
*/
longitude: number;
/**
* An optional address or description of the point of interest.
* @example "Albertinaplatz 2, 1010 Wien, Austria"
*/
address?: string;
/**
* A flexible container for any additional metadata from the data source.
* E.g. operational hours, tags, or other details.
*/
metadata?: Record<string, any>;
}
interface PictogramMarkerOptions {
/**
* Whether to add the pictogram description to the pictogram marker in case a description is available.
*
* @default false
*/
addDescription?: boolean;
/**
* Whether to bring the pictogram marker to the front when clicked.
*
* @default true
*/
bringToFrontOnClick?: boolean;
/**
* Whether to bring the pictogram marker to the front when hovered.
*
* @default true
*/
bringToFrontOnHover?: boolean;
/**
* Whether to bring the pictogram marker to the front when focused.
*
* @default true
*/
bringToFrontOnFocus?: boolean;
/**
* A callback function that is called when the pictogram marker is clicked.
*
* @default undefined
*/
onClick?: (pictogram: Pictogram, pointOfInterest?: PointOfInterest) => void;
}
/**
* A custom Leaflet layer that displays a pictogram marker at a specified geographical location.
*
* This marker supports accessibility features, customizable interactions, and flexible styling.
* It is designed to be used with the Leaflet library and integrates easily into any Leaflet map.
*/
declare class PictogramMarker extends L.Layer {
private readonly addDescription;
private readonly bringToFrontOnClick;
private readonly bringToFrontOnHover;
private readonly bringToFrontOnFocus;
private readonly onClick;
private readonly _latlng;
private readonly _pictogram;
private readonly _pointOfInterest;
private container?;
private box?;
private map;
getLatLng(): L.LatLng;
/**
* Constructs a new instance of the `PictogramMarker` class.
*
* @param latlng - The geographical coordinates where the marker should be placed.
* @param pictogram - The pictogram object containing the display data (e.g., URL, label, description).
* @param pointOfInterest - (Optional) The associated point of interest for this marker.
* @param options - (Optional) Configuration options for the marker's behavior and interactivity.
*
* @example
* ```typescript
* const latlng = L.latLng(48.20849, 16.37208);
* const pictogram = {
* id: '1',
* url: 'https://example.com/pictogram.png',
* displayText: 'Restaurant',
* description: 'A fine dining restaurant serving local cuisine.'
* };
*
* const options = {
* addDescription: true,
* bringToFrontOnClick: true,
* onClick: (pictogram) => {
* console.log('Pictogram clicked:', pictogram);
* }
* };
*
* const marker = new PictogramMarker(latlng, pictogram, undefined, options);
* marker.addTo(map);
* ```
*/
constructor(latlng: LatLngExpression, pictogram: Pictogram, pointOfInterest?: PointOfInterest, options?: PictogramMarkerOptions);
onAdd(map: Map): this;
onRemove(map: Map): this;
private updatePosition;
private setupInteractions;
private toggleInFront;
}
/**
* Options for querying points of interest within a specified geographic area.
*
* Implementations should adhere to the following behaviors:
* - If the `types` array is not provided, the query should include all available types.
* - If the `types` array is provided but is empty, the query should exclude all results (no types match).
* - The `limit` specifies the maximum number of results and should be respected when provided.
* - The `metadata` field allows for additional, implementation-specific query parameters.
*/
interface PointOfInterestQueryOptions {
/**
* A list of types or categories to filter the results.
* - If this field is omitted, the query should include all available types.
* - If this field is provided as an empty array, the query should return no results.
* Examples: ["restaurant", "park", "museum"].
*/
types?: string[];
/**
* The maximum number of results to return.
* If omitted, the implementation should return all results or the default maximum limit.
* Example: 50.
*/
limit?: number;
/**
* A flexible container for any additional options specific to the implementation.
* This can include fields such as `openNow` (only return currently open places), `radius`, or API-specific flags.
*/
metadata?: Record<string, any>;
}
/**
* Abstract interface for a service that fetches points of interest (POIs)
* within a specified geographic area.
*
* Implementations of this interface should:
* - Query a data source (e.g., Google Places API, Overpass API, or Apple Maps Server API).
* - Adhere to the filtering and behavior rules defined in `PointOfInterestQueryOptions`.
* - Return a list of POIs with standardized fields as defined in `PointOfInterest`.
*/
interface PointOfInterestService {
/**
* Fetches a list of points of interest within the given bounds.
*
* @param bounds - The geographic bounds for the query. Specifies the area to search for POIs.
* @param options - Optional filters and query settings.
* - If `types` is omitted, all available POI types should be included in the query.
* - If `types` is an empty array, no POIs should be returned.
* - If `limit` is specified, the implementation should respect this value.
* @returns A promise resolving to an array of POIs.
*/
getPointsOfInterest(bounds: LatLngBounds, options?: PointOfInterestQueryOptions): Promise<PointOfInterest[]>;
}
/**
* Options for configuring the OverpassPOIService.
*/
interface OverpassPOIServiceOptions {
/**
* The base URL of the Overpass API endpoint.
*
* @default "https://overpass-api.de/api/interpreter".
* @see https://wiki.openstreetmap.org/wiki/Overpass_API#Public_Overpass_API_instances Public Overpass API instances
*/
apiUrl?: string;
/**
* Default types of points of interest to query if none are provided in the {@link PointOfInterestQueryOptions}
* object in the {@link getPointsOfInterest} method.
*
* @default ["shop", "leisure"]
* @see https://wiki.openstreetmap.org/wiki/Key:shop
* @see https://wiki.openstreetmap.org/wiki/Key:leisure
*/
defaultTypes?: string[];
/**
* OpenStreetMap types to query.
* Possible values: "node", "way", "relation".
*
* @default ["node"]
* @see https://wiki.openstreetmap.org/wiki/Elements
*/
osmTypes?: string[];
/**
* Default number of points of interest to query if no limit is provided in the {@link PointOfInterestQueryOptions}
* object in the {@link getPointsOfInterest} method.
*
* @default 25
*/
defaultLimit?: number;
/**
* Maximum number of retries for requests in case of rate limiting or server errors.
*
* @default 3
*/
maxRetries?: number;
/**
* Timeout between retries in milliseconds.
*
* @default 1000
*/
retryDelay?: number;
/**
* Timeout for a single request in seconds.
*
* @default 25
*/
timeout?: number;
/**
* Whether to try to derive names for POIs with no name from their type.
*
* Some POIs may not have a name, but they have a type (e.g., "restaurant"). If this option is enabled,
* the service will try to derive a generic name for the {@link PointOfInterest} from the type (e.g., "Restaurant").
*
* @default true
*/
deriveNames?: boolean;
/**
* Whether to filter out POIs with no name.
*
* If this option is enabled, POIs with no name will be filtered out from the results.
* Otherwise, POIs with no name will be included in the results and will have the name "Unknown".
*
* @default true
*/
filterOutNoName?: boolean;
}
/**
* Abstract interface for a service that provides a {@link Pictogram} for a given {@link PointOfInterest}.
*/
interface PictogramService {
/**
* @description Fetches a pictogram for the given point of interest.
*
* @param poi The {@link PointOfInterest} to fetch a pictogram for.
* @returns A promise resolving to a {@link Pictogram} object or `undefined` if no pictogram is found.
*/
getPictogram(poi: PointOfInterest): Promise<Pictogram | undefined>;
}
/**
* @description Options for configuring the GlobalSymbolsPictogramService.
* @see https://globalsymbols.com/api/docs Global Symbols API documentation
*/
interface GlobalSymbolsPictogramServiceOptions {
/**
* @description The base URL of the Global Symbols API endpoint.
* @default "https://globalsymbols.com/api/v1/concepts/suggest".
*/
apiUrl?: string;
/**
* @description The symbol set the pictogram should be fetched from.
* @default "arasaac"
* @remarks Examples: `"arasaac"`, `"sclera"`, `"blissymbols"`, etc.
* @see https://globalsymbols.com/api/docs Global Symbols API documentation
* @see https://globalsymbols.com/api/v1/symbolsets GET all available symbol sets
*/
symbolSet?: string;
/**
* @description Include the type of the point of interest in the display text of the pictogram.
* @default false
*/
includeTypeInDisplayText?: boolean;
/**
* @description Include the type of the point of interest in the aria label of the pictogram.
* @default true
*/
includeTypeInAriaLabel?: boolean;
/**
* @description Cache strategy: "in-memory" (in-memory caching) or "local-storage" (persistent caching).
* @default "local-storage"
*/
cacheStrategy?: "in-memory" | "local-storage";
/**
* @description Cache expiration time in milliseconds.
* @default 604800000 (1 week)
*/
cacheExpiration?: number;
/**
* @description Cache prefix for the local storage cache.
* @default "global-symbols-pictogram-service"
* @remarks This is used to avoid conflicts with other local storage items.
*/
cachePrefix?: string;
}
/**
* Abstract interface for a service that sorts markers on a map.
*
* The sorting order does not affect the visual position of the markers on the map, but rather the order in which they
* are added to the DOM. This is relevant for screen readers and keyboard navigation.
*/
interface MarkerSortingService {
/**
* Sorts the given markers but DOES NOT add them to the map.
*
* @param markers The markers to sort.
* @param map The map is provided for context, e.g. to determine the current map bounds, mapping the coordinates
* to layer points, etc.
*
* @returns A promise resolving to the sorted markers.
*/
sortMarkers(markers: PictogramMarker[], map: L.Map): Promise<PictogramMarker[]>;
}
interface GridSortingServiceOptions {
/**
* The layout direction for the x-axis: "lr" (left-to-right) or "rl" (right-to-left).
*
* @default "lr"
*/
lr: "lr" | "rl";
/**
* The layout direction for the y-axis: "tb" (top-to-bottom) or "bt" (bottom-to-top).
*
* @default "tb"
*/
tb: "tb" | "bt";
/**
* The threshold in pixels to determine row separation.
*
* @default 64
*/
rowThreshold: number;
}
/**
* Options for configuring the Independo Maps plugin.
*/
interface IndependoMapsOptions {
/**
* Options for the {@link PictogramMarker}s created by the plugin.
*/
pictogramMarkerOptions?: PictogramMarkerOptions;
/**
* Options for configuring the default {@link OverpassPOIService}.
*
* These options are used if no custom `poiService` is provided.
* Allows customization of the Overpass API endpoint, default types, and other service-specific behaviors.
*
* @example
* ```typescript
* overpassServiceOptions: {
* apiUrl: "https://custom-overpass-api.com",
* defaultLimit: 50,
* defaultTypes: ["amenity", "shop", "tourism"]
* };
* ```
*/
overpassServiceOptions?: OverpassPOIServiceOptions;
/**
* Options for configuring the default {@link GlobalSymbolsPictogramService}.
*
* These options are used if no custom `pictogramService` is provided.
* Allows customization of behavior such as including types in display text and symbol set selection.
*
* @example
* ```typescript
* globalSymbolsServiceOptions: {
* includeTypeInDisplayText: true,
* symbolSet: "your-custom-symbolset"
* };
* ```
*/
globalSymbolsServiceOptions?: GlobalSymbolsPictogramServiceOptions;
/**
* Options for configuring the default {@link GridSortingService}.
*
* @remarks This option is used to define the layout direction of the pictograms when adding them to the DOM.
* This is relevant for screen readers and keyboard navigation. On the
* @example {lr: "lr", tb: "tb"}
*/
gridSortServiceOptions?: GridSortingServiceOptions;
/**
* Custom implementation of the {@link PointOfInterestService}.
*
* Use this field to provide your own service for fetching points of interest (POIs).
* If not provided, the plugin will default to an instance of {@link OverpassPOIService}.
*
* @example
* ```typescript
* poiService: new CustomPOIService();
* ```
*/
poiService?: PointOfInterestService;
/**
* Custom implementation of the {@link PictogramService}.
*
* Use this field to provide your own service for fetching pictograms for POIs.
* If not provided, the plugin will default to an instance of {@link GlobalSymbolsPictogramService}.
*
* @example
* ```typescript
* pictogramService: new CustomPictogramService();
* ```
*/
pictogramService?: PictogramService;
/**
* Custom implementation of the {@link MarkerSortingService}.
*
* Use this field to provide your own service for sorting markers on the map.
* If not provided, the plugin will default to an instance of {@link GridSortingService}.
*
* @remarks The sorting order does not affect the visual position of the markers on the map, but rather the order in
* which they are added to the DOM. This is relevant for screen readers and keyboard navigation. One can imagine
* use cases where the markers should be sorted in a specific order, e.g. by distance to the user's location.
*/
markerSortingService?: MarkerSortingService;
/**
* Debounce interval in milliseconds for updating the map after a move or zoom event.
*
* @remarks This interval prevents the map from updating too frequently and causing performance issues.
* @default 300
*/
debounceInterval?: number;
/**
* The default pictogram to use when no pictogram is found for a POI.
*
* @remarks This pictogram will be used when the pictogram service returns `undefined` for a POI. If no pictogram
* can be found for a POI and the default pictogram is not provided, the POI will not be displayed on the map.
* @default undefined
*/
defaultPictogram?: Pictogram;
}
declare class IndependoMaps {
private readonly debounceInterval;
private readonly defaultPictogram?;
private readonly map;
private readonly poiLayerGroup;
private readonly poiService;
private readonly pictogramService;
private readonly markerSortingService;
private readonly pictogramMarkerOptions?;
constructor(map: L.Map, options?: IndependoMapsOptions);
/**
* Updates the map by fetching POIs and adding corresponding markers.
*/
private updateMap;
}
/**
* Initializes the Independo Maps plugin on a {@link L.Map} and returns an instance of the plugin.
*
* @param map The {@link L.Map} to initialize the plugin on.
* @param options Optional {@link IndependoMapsOptions} to configure the plugin.
* @returns An instance of {@link IndependoMaps}.
*/
declare function initIndependoMaps(map: L.Map, options?: IndependoMapsOptions): IndependoMaps;
export { PictogramMarker, initIndependoMaps };