Skip to content

Commit

Permalink
chore: rename render functions + README improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
wazolab authored and frodrigo committed Sep 3, 2024
1 parent acc23ad commit 977dd20
Show file tree
Hide file tree
Showing 4 changed files with 159 additions and 109 deletions.
65 changes: 50 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ Render:
- Pin Marker on feature click

Allow visualization and interaction with all markers, even when superposed.
Can display and interact with small cluster without the need to zoom or TeritorioCluster.
Can display and interact with small cluster without the need to zoom or uncluster.

See the [Demo page](https://teritorio.github.io/maplibre-gl-teritorio-cluster/index.html).

Expand All @@ -29,15 +29,51 @@ import { Map } from "maplibre-gl"
import { TeritorioCluster } from 'maplibre-gl-teritorio-cluster'

const map = new Map({
container: "map_dom_el_id",
style: './style.json'
container: "map",
style: {
version: 8,
name: "Empty Style",
metadata: { "maputnik:renderer": "mlgljs" },
sources: {
points: {
type: "geojson",
cluster: true,
clusterRadius: 80,
clusterMaxZoom: 22,
data: {
type: "FeatureCollection",
features: [
{
type: "Feature",
properties: { id: 1 },
geometry: { type: "Point", coordinates: [0, 0] }
},
{
type: "Feature",
properties: { id: 2 },
geometry: { type: "Point", coordinates: [0, 1] }
}
]
}
}
},
glyphs: "https://orangemug.github.io/font-glyphs/glyphs/{fontstack}/{range}.pbf",
layers: [
{
id: "cluster",
type: "circle",
source: "points"
}
],
id: "muks8j3"
}
});

map.on('load', () => {
const TeritorioCluster = new TeritorioCluster(map, 'your_source_name', options)

// Get feature click event
TeritorioCluster.addEventListener('teritorioClick', (e) => {
TeritorioCluster.addEventListener('click', (e) => {
console.log(e)
})

Expand All @@ -53,7 +89,7 @@ map.on('load', () => {
})

// Create whatever HTML element you want as Cluster
const clusterMode = (element: HTMLDivElement, props: MapGeoJSONFeature['properties'], size?: number): void => {}
const clusterRender = (element: HTMLDivElement, props: MapGeoJSONFeature['properties'], size?: number): void => {}

// Create whatever HTML element you want as individual Marker
const displayMarker = (element: HTMLDivElement, feature: MapGeoJSONFeature, size?: number): void => {}
Expand All @@ -77,21 +113,20 @@ Create a new Maplibre GL JS plugin for feature (cluster / individual marker) ren
- `map`: [`maplibregl.Map`](https://maplibre.org/maplibre-gl-js/docs/API/classes/Map/) The Map object represents the map on your page
- `source`: `string` The ID of the vector tile or GeoJSON source to query
- `options`: `object` Options to configure the plugin
- `options.clusterMaxZoom`: `number` The cluster's max zoom (optional, default `17`)
- `options.clusterMode`: `(element: HTMLDivElement, props: MapGeoJSONFeature['properties'], size?: number): void` Custom function for cluster rendering (optional)
- `options.clusterSize`: `number` Set the size for default cluster rendering mode (optional, default `38`)
- `options.markerMode`: `(element: HTMLDivElement, feature: MapGeoJSONFeature, size?: number): void` Custom function for individual marker rendering (optional)
- `options.markerSize`: `number` Set the size for default individual marker rendering mode (optional, default `24`)
- `options.teritorioClusterMode`: `'circle'` TeritorioCluster rendering preset (optional)
- `options.teritorioClusterMaxLeaves`: `number` TeritorioCluster max leaves number (optional, default `5`)
- `options.pinMarkerMode`: `(coords: LngLatLike, offset: Point): Marker` Custom function for pin marker rendering (optional)
- `options.clusterMaxZoom`: `number` Zoom level at which we force the rendering of the Unfolded Cluster (default `17`)
- `options.clusterRenderFn`: `(element: HTMLDivElement, props: MapGeoJSONFeature['properties']): void` Cluster render function (default `src/utils/helpers.ts/clusterRenderDefault()`)
- `options.markerRenderFn`: `(element: HTMLDivElement, feature: MapGeoJSONFeature, markerSize: number): void` Individual Marker render function (default `src/utils/helpers.ts/markerRenderDefault()`)
- `options.unfoldedClusterRenderFn`: `(parent: HTMLDivElement, items: MapGeoJSONFeature[], markerSize: number, renderMarker: (feature: MapGeoJSONFeature) => HTMLDivElement, clickHandler: (e: Event, feature: MapGeoJSONFeature) => void) => void` Unfolded Cluster render function (default `src/utils/helpers.ts/unfoldedClusterRenderDefault()`)
- `src/utils/helpers.ts/unfoldedClusterRenderCircle()` Circular Unfolder Cluster render function
- `options.unfoldedClusterMaxLeaves`: `number` Unfolded Cluster max leaves number (optional, default `5`)
- `options.pinMarkerRenderFn`: `(coords: LngLatLike, offset: Point): Marker` Pin Marker render function (default `src/utils/helpers.ts/pinMarkerRenderDefault()`)

#### addEventListener

Listen to feature click and returns it for external control.
Listen to feature click and return a `maplibregl.Feature` for external control.

```js
TeritorioCluster.addEventListener('teritorioClick', (e) => {
TeritorioCluster.addEventListener('click', (e) => {
console.log(e.detail.selectedFeature)
})

Expand Down
26 changes: 14 additions & 12 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
<script type="module">
import { Map, NavigationControl, Marker } from "maplibre-gl"
import { TeritorioCluster } from './src'
import { buildCss } from './src/utils/helpers'
import { buildCss, unfoldedClusterRenderCircle as unfoldedClusterRender } from './src/utils/helpers'

const map = new Map({
hash: true,
Expand Down Expand Up @@ -61,17 +61,18 @@
map,
'earthquakes',
{
clusterMode: displayCluster,
markerMode: displayMarker,
teritorioClusterMode: 'circle',
pinMarkerMode: displayPinMarker
clusterRenderFn: clusterRender,
markerRenderFn: markerRender,
markerSize: 28,
unfoldedClusterRenderFn: unfoldedClusterRender,
pinMarkerRenderFn: pinMarkerRender
},
)

teritorioCluster.render()

// Get feature click event
teritorioCluster.addEventListener('teritorioClick', (e) => {
teritorioCluster.addEventListener('click', (e) => {
console.log(e.detail.selectedFeature)
})

Expand All @@ -87,7 +88,7 @@
});
})

const displayPinMarker = (coords, offset) => {
const pinMarkerRender = (coords, offset) => {
return new Marker({
scale: 1.3,
color: '#f44336',
Expand All @@ -97,7 +98,7 @@
.setOffset(offset)
}

const displayMarker = (element, feature, size) => {
const markerRender = (element, feature, markerSize) => {
element.textContent = feature.properties?.mag

buildCss(element, {
Expand All @@ -107,14 +108,15 @@
'align-items': 'center',
'display': 'flex',
'color': 'white',
'width': `${size}px`,
'height': `${size}px`,
'width': `${markerSize}px`,
'height': `${markerSize}px`,
'cursor': 'pointer'
});
}

const displayCluster = (element, props) => {
const clusterRender = (element, props) => {
element.innerHTML = props.point_count.toLocaleString();

buildCss(element, {
'background-color': 'white',
'border-radius': '100%',
Expand All @@ -132,4 +134,4 @@

</body>

</html>
</html>
119 changes: 67 additions & 52 deletions src/teritorioCluster.ts
Original file line number Diff line number Diff line change
@@ -1,70 +1,90 @@
import type { GeoJSONSource, LngLatLike, MapGeoJSONFeature } from 'maplibre-gl'
import { Marker, Point } from 'maplibre-gl'
import {
displayPinMarkerDefault,
displayTeritorioClusterInCircle,
displayTeritorioClusterDefault,
displayMarkerDefault,
displayClusterDefault
clusterRenderDefault,
markerRenderDefault,
pinMarkerRenderDefault,
unfoldedClusterRenderDefault
} from './utils/helpers';

type TeritorioClusterMode = 'default' | 'circle'
type ClusterMode = ((element: HTMLDivElement, props: MapGeoJSONFeature['properties'], size?: number) => void)
type MarkerMode = ((element: HTMLDivElement, feature: MapGeoJSONFeature, size?: number) => void)
type PinMarkerMode = ((coords: LngLatLike, offset: Point) => Marker)
type UnfoldedCluster = (
(
parent: HTMLDivElement,
items: MapGeoJSONFeature[],
markerSize: number,
renderMarker: (feature: MapGeoJSONFeature) => HTMLDivElement,
clickHandler: (e: Event, feature: MapGeoJSONFeature) => void
) => void
)
type ClusterRender = (
(
element: HTMLDivElement,
props: MapGeoJSONFeature['properties']
) => void
)
type MarkerRender = (
(
element: HTMLDivElement,
feature: MapGeoJSONFeature,
markerSize: number
) => void
)
type PinMarkerRender = (
(
coords: LngLatLike,
offset: Point
) => Marker
)

export class TeritorioCluster extends EventTarget {
map: maplibregl.Map
clusterLeaves: Map<string, MapGeoJSONFeature[]>
clusterMaxZoom: number
clusterMode?: ClusterMode
clusterSize: number
clusterRender?: ClusterRender
markers: { [key: string]: Marker }
markerMode?: MarkerMode
markersOnScreen: { [key: string]: Marker }
markerRender?: MarkerRender
markerSize: number
markersOnScreen: { [key: string]: Marker }
pinMarker: Marker | null
pinMarkerMode?: PinMarkerMode
pinMarkerRender?: PinMarkerRender
selectedClusterId: string | null
selectedFeatureId: string | null
sourceId: string
ticking: boolean
teritorioClusterMode: TeritorioClusterMode
teritorioClusterMaxLeaves: number
unfoldedClusterRender?: UnfoldedCluster
unfoldedClusterMaxLeaves: number

constructor(
map: maplibregl.Map,
source: string,
options?: {
clusterMaxZoom: number,
clusterMode: ClusterMode,
clusterSize: number
markerMode: MarkerMode,
markerSize: number,
teritorioClusterMode: TeritorioClusterMode,
teritorioClusterMaxLeaves: number,
pinMarkerMode: PinMarkerMode
clusterRenderFn: ClusterRender,
markerRenderFn: MarkerRender,
markerSize: number
unfoldedClusterRenderFn: UnfoldedCluster,
unfoldedClusterMaxLeaves: number,
pinMarkerRenderFn: PinMarkerRender
}
) {
super()

this.map = map
this.clusterLeaves = new Map<string, MapGeoJSONFeature[]>()
this.clusterMaxZoom = options?.clusterMaxZoom || 17
this.clusterMode = options?.clusterMode
this.clusterSize = options?.clusterSize || 38
this.clusterRender = options?.clusterRenderFn
this.markers = {}
this.markerMode = options?.markerMode
this.markersOnScreen = {}
this.markerRender = options?.markerRenderFn
this.markerSize = options?.markerSize || 24
this.markersOnScreen = {}
this.pinMarker = null
this.pinMarkerMode = options?.pinMarkerMode
this.pinMarkerRender = options?.pinMarkerRenderFn
this.selectedClusterId = null
this.selectedFeatureId = null
this.sourceId = source
this.ticking = false
this.teritorioClusterMode = options?.teritorioClusterMode || 'default'
this.teritorioClusterMaxLeaves = options?.teritorioClusterMaxLeaves || 5
this.unfoldedClusterRender = options?.unfoldedClusterRenderFn
this.unfoldedClusterMaxLeaves = options?.unfoldedClusterMaxLeaves || 5

this.map.on('click', this.onClick)
}
Expand All @@ -84,45 +104,40 @@ export class TeritorioCluster extends EventTarget {
}

renderPinMarker = (coords: LngLatLike, offset: Point = new Point(0, 0)) => {
return !this.pinMarkerMode
? displayPinMarkerDefault(coords, offset)
: this.pinMarkerMode(coords, offset)
return !this.pinMarkerRender
? pinMarkerRenderDefault(coords, offset)
: this.pinMarkerRender(coords, offset)
}

renderMarker = (feature: MapGeoJSONFeature) => {
const element = document.createElement('div')
element.id = this.getFeatureId(feature)

!this.markerMode
? displayMarkerDefault(element, this.markerSize)
: this.markerMode(element, feature, this.markerSize)
!this.markerRender
? markerRenderDefault(element, this.markerSize)
: this.markerRender(element, feature, this.markerSize)

return element
}

renderTeritorioCluster = (id: string, leaves: MapGeoJSONFeature[]) => {
renderUnfoldedCluster = (id: string, leaves: MapGeoJSONFeature[]) => {
const element = document.createElement('div')
element.id = id
element.classList.add('teritorio-cluster')

switch (this.teritorioClusterMode) {
case 'circle':
displayTeritorioClusterInCircle(element, leaves, this.markerSize, this.renderMarker, this.featureClickHandler)
break
default:
displayTeritorioClusterDefault(element, leaves, this.renderMarker, this.featureClickHandler)
break
}
!this.unfoldedClusterRender
? unfoldedClusterRenderDefault(element, leaves, this.renderMarker, this.featureClickHandler)
: this.unfoldedClusterRender(element, leaves, this.markerSize, this.renderMarker, this.featureClickHandler)

return element
}

renderCluster = (props: MapGeoJSONFeature['properties']) => {
const element = document.createElement('div')

!this.clusterMode
? displayClusterDefault(element, props, this.clusterSize)
: this.clusterMode(element, props)
!this.clusterRender
? clusterRenderDefault(element, props)
: this.clusterRender(element, props)

return element;
}
Expand Down Expand Up @@ -185,8 +200,8 @@ export class TeritorioCluster extends EventTarget {
let element: HTMLDivElement
const leaves = this.clusterLeaves.get(id)

if (leaves && ((leaves.length <= this.teritorioClusterMaxLeaves) || maxZoomLimit)) {
element = this.renderTeritorioCluster(id, leaves)
if (leaves && ((leaves.length <= this.unfoldedClusterMaxLeaves) || maxZoomLimit)) {
element = this.renderUnfoldedCluster(id, leaves)
} else {
// Create default HTML Cluster
element = this.renderCluster(props)
Expand Down Expand Up @@ -321,7 +336,7 @@ export class TeritorioCluster extends EventTarget {
this.pinMarker = null
}

// If element is within TeritorioCluster
// If element is within Unfolded Cluster
if (!markerOnScreen && clusterId) {
this.selectedClusterId = clusterId

Expand All @@ -336,7 +351,7 @@ export class TeritorioCluster extends EventTarget {

this.selectedFeatureId = clickedEl.id

const event = new CustomEvent("teritorioClick", {
const event = new CustomEvent("click", {
detail: {
selectedFeature: feature,
}
Expand Down
Loading

0 comments on commit 977dd20

Please sign in to comment.