220 lines
6.4 KiB
TypeScript
220 lines
6.4 KiB
TypeScript
import maplibregl from 'maplibre-gl';
|
|
import { IntaleqColors } from './constants';
|
|
import { PolylineUtils } from './utils';
|
|
|
|
export interface IntaleqMapOptions extends Omit<maplibregl.MapOptions, 'container'> {
|
|
container: string | HTMLElement;
|
|
apiKey: string;
|
|
styleType?: 'obsidian' | 'light' | 'hybrid' | 'satellite';
|
|
}
|
|
|
|
export interface MarkerOptions extends maplibregl.MarkerOptions {
|
|
position: [number, number];
|
|
}
|
|
|
|
export interface PolylineOptions {
|
|
id: string;
|
|
coordinates: [number, number][];
|
|
color?: string;
|
|
width?: number;
|
|
opacity?: number;
|
|
}
|
|
|
|
export interface PolygonOptions {
|
|
id: string;
|
|
rings: [number, number][][]; // First is outer, others are holes
|
|
fillColor?: string;
|
|
fillOpacity?: number;
|
|
outlineColor?: string;
|
|
}
|
|
|
|
export interface CircleOptions {
|
|
id: string;
|
|
position: [number, number];
|
|
radiusMetres: number;
|
|
color?: string;
|
|
opacity?: number;
|
|
strokeColor?: string;
|
|
strokeWidth?: number;
|
|
}
|
|
|
|
/**
|
|
* Intaleq Maps JS SDK
|
|
* Optimized for Jordan & Syria mapping services.
|
|
*/
|
|
export class IntaleqMap extends maplibregl.Map {
|
|
private readonly intaleqApiKey: string;
|
|
private addedIntaleqIds: Set<string> = new Set();
|
|
|
|
constructor(options: IntaleqMapOptions) {
|
|
const { apiKey, styleType = 'obsidian', ...mapOptions } = options;
|
|
|
|
const styleUrl = `https://maps.intaleq.com/styles/${styleType}/style.json?key=${apiKey}`;
|
|
|
|
super({
|
|
...mapOptions,
|
|
style: styleUrl,
|
|
hash: mapOptions.hash ?? true,
|
|
center: mapOptions.center ?? [35.9239, 31.9522], // Default to Amman
|
|
zoom: mapOptions.zoom ?? 12,
|
|
transformRequest: (url: string) => {
|
|
if (url.includes('intaleq.com')) {
|
|
const separator = url.includes('?') ? '&' : '?';
|
|
return {
|
|
url: `${url}${separator}key=${apiKey}`
|
|
};
|
|
}
|
|
return { url };
|
|
}
|
|
});
|
|
|
|
this.intaleqApiKey = apiKey;
|
|
}
|
|
|
|
/**
|
|
* Helper to search for places using Intaleq Geocoding API
|
|
*/
|
|
async search(query: string) {
|
|
const response = await fetch(`https://map-saas.intaleq.com/v1/geocoding/search?q=${encodeURIComponent(query)}&key=${this.intaleqApiKey}`);
|
|
return response.json();
|
|
}
|
|
|
|
/**
|
|
* Helper to get routes using Intaleq Routing API
|
|
*/
|
|
async getRoute(start: [number, number], end: [number, number], profile: 'car' | 'bike' | 'foot' = 'car') {
|
|
const response = await fetch(`https://map-saas.intaleq.com/v1/routing/route?start=${start.join(',')}&end=${end.join(',')}&profile=${profile}&key=${this.intaleqApiKey}`);
|
|
return response.json();
|
|
}
|
|
|
|
// --- Friendly Abstractions (Transformation Layer) ---
|
|
|
|
/**
|
|
* Add a high-quality marker to the map (HTML-based).
|
|
*/
|
|
addIntaleqMarker(options: MarkerOptions): maplibregl.Marker {
|
|
const marker = new maplibregl.Marker(options)
|
|
.setLngLat(options.position)
|
|
.addTo(this);
|
|
return marker;
|
|
}
|
|
|
|
/**
|
|
* Add a polyline (route line) to the map.
|
|
*/
|
|
addIntaleqPolyline(options: PolylineOptions) {
|
|
const { id, coordinates, color, width, opacity } = options;
|
|
this.addSource(id, {
|
|
type: 'geojson',
|
|
data: {
|
|
type: 'Feature',
|
|
properties: {},
|
|
geometry: {
|
|
type: 'LineString',
|
|
coordinates: coordinates
|
|
}
|
|
}
|
|
});
|
|
|
|
this.addLayer({
|
|
id: id,
|
|
type: 'line',
|
|
source: id,
|
|
layout: {
|
|
'line-join': 'round',
|
|
'line-cap': 'round'
|
|
},
|
|
paint: {
|
|
'line-color': color ?? IntaleqColors.primaryBlue,
|
|
'line-width': width ?? 5,
|
|
'line-opacity': opacity ?? 1.0
|
|
}
|
|
});
|
|
this.addedIntaleqIds.add(id);
|
|
}
|
|
|
|
/**
|
|
* Add a polygon (with hole support) to the map.
|
|
*/
|
|
addIntaleqPolygon(options: PolygonOptions) {
|
|
const { id, rings, fillColor, fillOpacity, outlineColor } = options;
|
|
this.addSource(id, {
|
|
type: 'geojson',
|
|
data: {
|
|
type: 'Feature',
|
|
properties: {},
|
|
geometry: {
|
|
type: 'Polygon',
|
|
coordinates: rings
|
|
}
|
|
}
|
|
});
|
|
|
|
this.addLayer({
|
|
id: id,
|
|
type: 'fill',
|
|
source: id,
|
|
paint: {
|
|
'fill-color': fillColor ?? IntaleqColors.fillGray,
|
|
'fill-opacity': fillOpacity ?? 0.4,
|
|
'fill-outline-color': outlineColor ?? IntaleqColors.primaryBlue
|
|
}
|
|
});
|
|
this.addedIntaleqIds.add(id);
|
|
}
|
|
|
|
/**
|
|
* Add a circle to the map.
|
|
* Note: radius is in pixels for standard GL circles.
|
|
* For geographic circles, consider using polygon buffers.
|
|
*/
|
|
addIntaleqCircle(options: CircleOptions) {
|
|
const { id, position, radiusMetres, color, opacity, strokeColor, strokeWidth } = options;
|
|
|
|
this.addSource(id, {
|
|
type: 'geojson',
|
|
data: {
|
|
type: 'Feature',
|
|
properties: {},
|
|
geometry: {
|
|
type: 'Point',
|
|
coordinates: position
|
|
}
|
|
}
|
|
});
|
|
|
|
this.addLayer({
|
|
id: id,
|
|
type: 'circle',
|
|
source: id,
|
|
paint: {
|
|
'circle-radius': radiusMetres / 10, // Rough pixel approximation for demo
|
|
'circle-color': color ?? IntaleqColors.primaryBlue,
|
|
'circle-opacity': opacity ?? 0.6,
|
|
'circle-stroke-color': strokeColor ?? '#ffffff',
|
|
'circle-stroke-width': strokeWidth ?? 2
|
|
}
|
|
});
|
|
this.addedIntaleqIds.add(id);
|
|
}
|
|
|
|
/**
|
|
* Remove an object added via Intaleq helpers.
|
|
*/
|
|
removeIntaleqObject(id: string) {
|
|
if (this.getLayer(id)) this.removeLayer(id);
|
|
if (this.getSource(id)) this.removeSource(id);
|
|
this.addedIntaleqIds.delete(id);
|
|
}
|
|
|
|
/**
|
|
* Clear all objects added via Intaleq helpers.
|
|
*/
|
|
clearAll() {
|
|
this.addedIntaleqIds.forEach(id => this.removeIntaleqObject(id));
|
|
this.addedIntaleqIds.clear();
|
|
}
|
|
}
|
|
|
|
export { maplibregl, IntaleqColors, PolylineUtils };
|