-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
4 changed files
with
164 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
import { describe, expect, it } from 'vitest'; | ||
import { render } from '@testing-library/react'; | ||
import BaseMap from './'; | ||
|
||
describe('BaseMap', () => { | ||
it('renders the component', () => { | ||
const { container } = render(<BaseMap />); | ||
expect(container.firstChild).toBeDefined(); | ||
}); | ||
|
||
it('uses the amsterdam base tile', () => { | ||
const { container } = render(<BaseMap />); | ||
|
||
// Only test on the less dynamic part of the URL | ||
const imgSrc = ( | ||
container.querySelector('.leaflet-tile-container img') as HTMLImageElement | ||
)?.src.substring(0, 38); | ||
|
||
expect( | ||
imgSrc.match( | ||
/https:\/\/(t1)|(t2)|(t3)|(t4)\.data.amsterdam.nl\/topo_rd\//g | ||
) | ||
).not.toEqual(null); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
import L from 'leaflet'; | ||
import { MapContainer, TileLayer } from 'react-leaflet'; | ||
import 'leaflet/dist/leaflet.css'; | ||
import getCrsRd from '@/utils/getCrsRd'; | ||
import styles from './styles.module.css'; | ||
|
||
const BaseLayer = (): JSX.Element => ( | ||
<div className={styles.container}> | ||
<MapContainer | ||
center={L.latLng([52.370216, 4.895168])} | ||
zoom={13} | ||
maxBounds={[ | ||
[52.25168, 4.64034], | ||
[52.50536, 5.10737], | ||
]} | ||
crs={getCrsRd()} | ||
> | ||
<TileLayer | ||
url="https://{s}.data.amsterdam.nl/topo_rd/{z}/{x}/{y}.png" | ||
subdomains={['t1', 't2', 't3', 't4']} | ||
tms | ||
/> | ||
</MapContainer> | ||
</div> | ||
); | ||
|
||
export default BaseLayer; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
import { Canvas, Meta, Source, Story } from '@storybook/blocks'; | ||
import * as MarkerStories from './index.stories'; | ||
import Marker from '@/pages/Marker/Marker?raw'; | ||
import styles from '@/pages/Marker/styles.module.css?raw'; | ||
import customMarker from '@/pages/Marker/icons/customMarker?raw'; | ||
import icon from '@/assets/icons/map-marker.svg?raw'; | ||
|
||
<Meta of={MarkerStories} /> | ||
|
||
# Marker | ||
## Requirements | ||
|
||
- This example is built upon the [BaseMap component example](../?path=/docs/react-baselayer--docs). | ||
- [See global requirements list](../?path=/docs/global-requirements--docs) | ||
|
||
## Description | ||
|
||
A marker is used to display a location on a map. By default, a marker is a HTML image element rendered inside the parent map DOM element. This marker element can be configured, extended and (like in this example) replaced with another icon. | ||
|
||
In this code example, the default Leaflet marker ([example](https://leafletjs.com/examples/layers-control/)) is replaced with the `L.icon` ([docs](https://leafletjs.com/reference.html#icon)); another alternative to this is the `L.divIcon` ([docs](https://leafletjs.com/reference.html#divicon)). [Read more Leaflet icons here](../?path=/docs/icons--docs). | ||
|
||
The primary code in regards to creating a Leaflet marker, is lines 50-59: | ||
|
||
```js | ||
useEffect(() => { | ||
if (mapInstance) { | ||
const marker = L.marker(L.latLng([52.370216, 4.895168]), { | ||
// There are many more options to choose from @see https://leafletjs.com/reference.html#marker | ||
icon: customMarker | ||
}).addTo(mapInstance) | ||
.on('click', () => alert('Marker click!')); | ||
setMarkerInstance(marker); | ||
} | ||
}, [mapInstance]); | ||
``` | ||
|
||
This creates a marker at the coordinates (52.370216, 4.895168), which is added to the map (via the `addTo` method) and includes an example event listener that will be triggered on marker clicks. Then in the rest of the code, this marker element, can be referred to via the `markerInstance` state variable and interacted with using Leaflet methods. | ||
|
||
A Leaflet marker element consists of [events](https://leafletjs.com/reference.html#marker-move), [methods](https://leafletjs.com/reference.html#marker-l-marker) and [options](https://leafletjs.com/reference.html#marker-icon). | ||
|
||
### Large numbers of markers can lead to degraded performance | ||
|
||
A standard Leaflet marker is a HTML image element. Therefore, if there are 100 markers, then there are 100 HTML image elements - each one with its own events, listeners and side-effects - another element to add to the DOM tree. Modern browsers and devices are quite efficient so negative performance often won't be noticed until you are handling tens of thousands of markers. | ||
|
||
The real solution to this is to ideally never render so many markers simultaneously. However, with some APIs that isn't always an easy option. This is where clustering ideally should be implemented or the `preferCanvas` option is set to `true` when creating your Leaflet map. | ||
|
||
The `preferCanvas` instructs Leaflet to use the HTML Canvas element, which performs a lot quicker than the traditional HTML DOM tree. [See docs](https://leafletjs.com/reference.html#map-prefercanvas). | ||
|
||
## Usage Scenarios | ||
|
||
- **Location Pins**: Highlighting specific locations such as restaurants, shops, landmarks, etc. | ||
- **Data Visualization**: Displaying data points like weather stations, earthquake epicenters, etc. | ||
- **Interactive Maps**: Providing interactive elements for user interaction, such as selecting meeting points or identifying places of interest. | ||
|
||
## How to implement | ||
|
||
To implement a Leaflet marker, there are four files: | ||
|
||
1. The React component | ||
* [Marker.tsx](#1-markertsx) | ||
* *This is based on the [BaseMap component example](../?path=/docs/react-baselayer--docs) so includes a dependency on [`utils/getCrsRd`](../?path=/docs/react-baselayer--docs#1-getcrsrdts).* | ||
2. The custom icon | ||
* [icons/customMarker.tsx](#1-iconscustommarkertsx) | ||
3. The CSS styles (1 file) | ||
* [styles.module.css](#1-stylesmodulecss) | ||
4. Image | ||
* [assets/icons/map-marker.svg](#1-map-markersvg) | ||
|
||
## Usage | ||
|
||
### React Components | ||
|
||
#### 1. Marker.tsx | ||
|
||
<Source code={Marker} /> | ||
|
||
### Custom icon | ||
|
||
#### 1. icons/customMarker.tsx | ||
|
||
<Source code={customMarker} /> | ||
|
||
### CSS Styling | ||
|
||
#### 1. styles.module.css | ||
|
||
<Source code={styles} /> | ||
|
||
### Assets | ||
|
||
#### 1. map-marker.svg | ||
|
||
<Source code={icon} /> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
import type { Meta, StoryObj } from '@storybook/react'; | ||
import Marker from '@/pages/Marker/Marker'; | ||
|
||
const meta = { | ||
title: 'React/Marker', | ||
component: Marker, | ||
parameters: { | ||
layout: 'fullscreen', | ||
options: { | ||
panelPosition: 'bottom', | ||
bottomPanelHeight: 0, | ||
}, | ||
}, | ||
} satisfies Meta<typeof Marker>; | ||
|
||
export default meta; | ||
type Story = StoryObj<typeof meta>; | ||
|
||
export const Base: Story = {}; |