You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
101 lines
3.6 KiB
TypeScript
101 lines
3.6 KiB
TypeScript
import { CRS, TileLayer, type WMSOptions, type WMSParams } from 'leaflet';
|
|
import { bool, str, type PropDef, type Positional } from '../core/props.ts';
|
|
import { tileLayerProps, urlProp, type Sourced } from '../core/shared-props.ts';
|
|
import {
|
|
WithProps,
|
|
type LeafletAddEventListener,
|
|
type LeafletElementConstructor,
|
|
type LeafletRemoveEventListener,
|
|
} from '../core/with-props.ts';
|
|
import type { TileLayerEvents } from '../core/event-types.ts';
|
|
import type { LeafletCRSChangedEvent } from '../core/register.ts';
|
|
|
|
// WMS request parameters have no individual setters -- they're merged into the
|
|
// query string through setParams().
|
|
function param<T>(key: keyof WMSParams): (obj: TileLayer.WMS, value: T) => void {
|
|
return (obj, value) => {
|
|
obj.setParams({ [key]: value } as unknown as WMSParams);
|
|
};
|
|
}
|
|
|
|
// `crs` takes a CRS instance, not a primitive. The attribute only covers the
|
|
// 4 CRSes Leaflet ships by name -- a CRS Leaflet doesn't ship comes from a
|
|
// nested child announcing itself via leaflet-crs-changed instead (see
|
|
// #onCrsChanged), which takes priority over this when present. Constructor-
|
|
// only, like the rest of tileLayerProps -- Leaflet has no setter for it.
|
|
const NAMED_CRS = {
|
|
EPSG3857: CRS.EPSG3857,
|
|
EPSG4326: CRS.EPSG4326,
|
|
EPSG3395: CRS.EPSG3395,
|
|
Simple: CRS.Simple,
|
|
};
|
|
type CrsName = keyof typeof NAMED_CRS;
|
|
|
|
const crsProp: PropDef<CRS> = {
|
|
default: CRS.EPSG3857,
|
|
decode: (raw) => NAMED_CRS[raw as CrsName] ?? CRS.EPSG3857,
|
|
encode: (value) =>
|
|
value === CRS.EPSG3857
|
|
? null
|
|
: ((Object.keys(NAMED_CRS) as CrsName[]).find((name) => NAMED_CRS[name] === value) ?? null),
|
|
};
|
|
|
|
const PROPS: {
|
|
url: Positional<string, Sourced>;
|
|
} & typeof tileLayerProps & {
|
|
layers: PropDef<string, TileLayer.WMS>;
|
|
styles: PropDef<string, TileLayer.WMS>;
|
|
format: PropDef<string, TileLayer.WMS>;
|
|
transparent: PropDef<boolean, TileLayer.WMS>;
|
|
version: PropDef<string, TileLayer.WMS>;
|
|
uppercase: PropDef<boolean>;
|
|
crs: PropDef<CRS>;
|
|
} = {
|
|
url: urlProp,
|
|
...tileLayerProps,
|
|
layers: str('', { set: param('layers') }),
|
|
styles: str('', { set: param('styles') }),
|
|
format: str('image/jpeg', { set: param('format') }),
|
|
transparent: bool(false, { set: param('transparent') }),
|
|
version: str('1.1.1', { set: param('version') }),
|
|
uppercase: bool(),
|
|
crs: crsProp,
|
|
};
|
|
const Base: LeafletElementConstructor<TileLayer.WMS, typeof PROPS> = WithProps(PROPS);
|
|
|
|
export default class LeafletTileLayerWMSElement extends Base {
|
|
declare readonly leafletObject?: TileLayer.WMS;
|
|
declare addEventListener: LeafletAddEventListener<TileLayerEvents>;
|
|
declare removeEventListener: LeafletRemoveEventListener<TileLayerEvents>;
|
|
|
|
// Set by a nested CRS-providing child; overrides the `crs` attribute's
|
|
// named lookup when present.
|
|
#childCrs?: CRS;
|
|
|
|
createLeafletObject(options: WMSOptions): TileLayer.WMS {
|
|
return new TileLayer.WMS(
|
|
this.url,
|
|
this.#childCrs ? { ...options, crs: this.#childCrs } : options,
|
|
);
|
|
}
|
|
|
|
connectedCallback(): void {
|
|
super.connectedCallback();
|
|
this.addEventListener('leaflet-crs-changed', this.#onCrsChanged);
|
|
}
|
|
|
|
disconnectedCallback(): void {
|
|
this.removeEventListener('leaflet-crs-changed', this.#onCrsChanged);
|
|
super.disconnectedCallback();
|
|
}
|
|
|
|
// A CRS Leaflet doesn't ship comes from any custom element nested here
|
|
// that fires this event -- no base class required, just this event shape
|
|
// (see register.ts). `crs` is constructor-only, so picking it up means
|
|
// rebuilding the whole layer.
|
|
#onCrsChanged = (e: LeafletCRSChangedEvent) => {
|
|
this.#childCrs = e.detail.crs ?? undefined;
|
|
this.recreateLeafletObject();
|
|
};
|
|
}
|