Overlay Layer
An overlay layer can be added to display markers.
Marker
The Marker
component creates a div
, the top left corner
of which is the screen point corresponding to the coordinate.
Markers have a render function which is used to display the desired component. The library comes with a standard pin.
- JS
- TS
import {
Map,
Marker,
OverlayLayer,
SVGPin,
} from '@jetblack/map'
const places = {
greenwichObservatory: {
latitude: 51.47684676353231,
longitude: -0.0005261695762532147,
},
buckinghamPalace: {
latitude: 51.501200111998415,
longitude: -0.14182556179982908,
}
}
export default function App() {
return (
<Map
width='600px'
height='400px'
center={places.greenwichObservatory}
zoom={11}
>
<OverlayLayer>
<Marker
coordinate={places.greenwichObservatory}
render={point => <SVGPin point={point} />}
/>
<Marker
coordinate={places.buckinghamPalace}
render={point => <SVGPin point={point} />}
/>
</OverlayLayer>
</Map>
)
}
import {
Coordinate,
Map,
Marker,
OverlayLayer,
SVGPin,
} from '@jetblack/map'
const places: { [ name: string ]: Coordinate } = {
greenwichObservatory: {
latitude: 51.47684676353231,
longitude: -0.0005261695762532147,
},
buckinghamPalace: {
latitude: 51.501200111998415,
longitude: -0.14182556179982908,
}
}
export default function App() {
return (
<Map
width='600px'
height='400px'
center={places.greenwichObservatory}
zoom={11}
>
<OverlayLayer>
<Marker
coordinate={places.greenwichObservatory}
render={point => <SVGPin point={point} />}
/>
<Marker
coordinate={places.buckinghamPalace}
render={point => <SVGPin point={point} />}
/>
</OverlayLayer>
</Map>
)
}
Custom Markers
The following code demonstrates a custom marker of a circle centered around the coordinate.
There are a couple of gotchas here.
- As the containing
div
is centered around the top left of the coordinate, the circle must be shifted up and left. This is done with thetransform
style. - Mouse events are consumed by the map to allow interaction. If the marker
should receive mouse events the
pointerEvents
attribute should be set toauto
.
export interface CustomMarkerProps {
point: Point
radius?: number
strokeWidth?: number
color?: string
}
function CircleMarker({
radius = 10,
strokeWidth = 2,
color = 'red'
}: CustomMarkerProps) {
const size = radius * 2 + strokeWidth * 2
return (
<svg
width={size}
height={size}
xmlns="http://www.w3.org/2000/svg"
style={{
pointerEvents: 'auto',
transform: `translate(${-size / 2}px, ${-size / 2}px)`,
}}
>
<circle
cx={size / 2}
cy={size / 2}
r={radius}
stroke={color}
strokeWidth={2}
fill="none"
/>
</svg>
)
}
This marker could be incorporated into the map as follows:
<Map ref={ref} width="600px" height="400px" center={center} zoom={zoom}>
<OverlayLayer>
<Marker
coordinate={places.greenwichObservatory}
render={point => <CircleMarker point={point} />}
/>
<Marker
coordinate={places.greenwichObservatory}
render={point => <CircleMarker point={point} radius={15} />}
/>
</OverlayLayer>
</Map>
Using the MapContext
All map children have access to the map context.
When there are a lot of markers the map gets very cluttered when zoomed out. The following example uses the map context to scale the circle according to the zoom level. The farther out the map is zoomed the smaller the circle is.
export interface ScaledMarkerProps {
point: Point
color?: string
radius?: number
}
function ScaledMarker({ radius = 5, color = 'red' }: ScaledMarkerProps) {
// Get zoom info from the context.
const {
zoom,
tileProvider: { minZoom, maxZoom },
} = useContext(MapContext)
// Calculate the scale and apply it to the radius of the circle.
const scale = (zoom - minZoom) / (maxZoom - minZoom)
const scaledRadius = radius + 20 * radius * scale
const strokeWidth = 2
const size = scaledRadius * 2 + strokeWidth * 2
return (
<svg
className={classNames.customMarker}
width={size}
height={size}
xmlns="http://www.w3.org/2000/svg"
style={{
pointerEvents: 'auto',
transform: `translate(${-size / 2}px, ${-size / 2}px)`,
}}
>
<circle
cx={size / 2}
cy={size / 2}
r={scaledRadius}
stroke={color}
strokeWidth={2}
fill="none"
/>
</svg>
)
}