Skip to content

Commit 3a017e1

Browse files
committed
Added default layer and view to map.tsx, so that users doesn't need to add them
1 parent 93e071f commit 3a017e1

36 files changed

+195
-195
lines changed

src/lib/Map.tsx

Lines changed: 48 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,62 @@
11
import {
2-
forwardRef, useImperativeHandle,
3-
useRef, useEffect, useState,
4-
createContext, useContext,
2+
forwardRef,
3+
useImperativeHandle,
4+
useRef,
5+
useEffect,
6+
useState,
7+
createContext,
8+
useContext,
59
} from 'react';
6-
import * as ol from 'ol';
710
import {defaults as defaultControls} from 'ol/control/defaults.js';
8-
import VectorLayer from 'ol/layer/Vector';
9-
import VectorSource from 'ol/source/Vector';
11+
import { Map as OlMap } from 'ol';
12+
import View from 'ol/View';
1013
import './ol.css';
1114
import './Map.css';
15+
import TileLayer from 'ol/layer/Tile';
16+
import { OSM } from 'ol/source';
1217

13-
const MapContext = createContext<ol.Map>(undefined);
18+
interface MapProps {
19+
children?: React.ReactNode;
20+
[key: string]: any; // Allow other OpenLayers Map options
21+
}
22+
23+
const MapContext = createContext<OlMap | undefined>(undefined);
24+
25+
export const useMap = () => {
26+
const map = useContext(MapContext);
27+
// if (!map) throw new Error('useMap must be used within a Map component');
28+
return map;
29+
};
1430

15-
export const Map = forwardRef( (props:any, ref:any) => {
16-
const [map, setMap] = useState<ol.Map>();
17-
const mapRef = useRef<any>();
31+
export const Map = forwardRef<OlMap | undefined, MapProps>((props, ref) => {
32+
const [map, setMap] = useState<OlMap>();
33+
const mapRef = useRef<HTMLDivElement>(null); // Type the DOM ref
1834

1935
useImperativeHandle(ref, () => map, [map]);
2036

21-
const markerLayer = new VectorLayer({
22-
source: new VectorSource({ features: [] }),
23-
properties: {key: 'markerLayer'},
24-
zIndex: 1,
25-
});
37+
const mounted = useRef(false);
38+
const defaultLayer = new TileLayer({ source: new OSM() });
2639

2740
useEffect(() => {
28-
if (!mapRef.current) return;
29-
const mapProps = {...{
30-
target: mapRef.current,
31-
layers: [markerLayer],
32-
controls: defaultControls(),
33-
}, ...props}
34-
const notInitialized = mapRef.current?.children?.length === 0;
35-
if (notInitialized) { // Do not render twice with <React.StriceMode>
36-
const olMap = new ol.Map(mapProps);
37-
setMap(olMap);
38-
}
41+
if (!mapRef.current || mounted.current) return;
42+
const mapProps = {
43+
...{
44+
target: mapRef.current,
45+
layers: [defaultLayer],
46+
controls: defaultControls(),
47+
view: new View({
48+
center: [0, 0], // Default to [lon, lat]
49+
zoom: 2, // Default zoom level
50+
}),
51+
},
52+
...props, // Override with props.view if provided
53+
};
54+
const olMap = new OlMap(mapProps);
55+
setMap(olMap);
56+
mounted.current = true;
57+
return () => {
58+
olMap.setTarget(null);
59+
};
3960
}, []);
4061

4162
return (
@@ -45,7 +66,4 @@ export const Map = forwardRef( (props:any, ref:any) => {
4566
</div>
4667
</MapContext.Provider>
4768
);
48-
}
49-
);
50-
51-
export const useMap = () => useContext(MapContext);
69+
});

src/lib/Marker.tsx

Lines changed: 42 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
import { useEffect } from 'react';
2-
import * as ol from 'ol';
3-
import {fromLonLat, toLonLat} from 'ol/proj';
2+
import { Feature as OlFeature } from 'ol';
3+
import { fromLonLat, toLonLat } from 'ol/proj';
44
import Style from 'ol/style/Style';
55
import Icon from 'ol/style/Icon';
66
import Point from 'ol/geom/Point';
77
import VectorLayer from 'ol/layer/Vector';
88
import { useMap } from './Map';
99
import { getLonLat } from './util';
10+
import VectorSource from 'ol/source/Vector';
1011

1112
export function getMarkerImage(color='red', text='A') {
1213
return `data:image/svg+xml,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22UTF-8%22%3F%3E` +
@@ -28,6 +29,43 @@ function getNextChar(char) {
2829
}
2930
}
3031

32+
33+
function addMarker(markerLayer, lonLat, color, char) {
34+
const markerFeature = new OlFeature({
35+
geometry: new Point(fromLonLat(lonLat))
36+
});
37+
markerFeature.setStyle(new Style({
38+
image: new Icon({
39+
scale: 1,
40+
anchor: [0.5,40],
41+
anchorXUnits: 'fraction',
42+
anchorYUnits: 'pixels',
43+
src: getMarkerImage(color, char)
44+
}),
45+
}));
46+
47+
markerLayer.getSource().addFeature(markerFeature);
48+
return markerFeature;
49+
}
50+
51+
function getMarkerLayer(map) {
52+
const markerLayer = map.getLayers().getArray()
53+
.find(el => el.get('key') === 'markerLayer') as VectorLayer;
54+
if (markerLayer) {
55+
return markerLayer;
56+
}
57+
58+
// container of markers, this can be set when marker is added, so that I can remove it here.
59+
const newMarkerLayer = new VectorLayer({
60+
source: new VectorSource({ features: [] }),
61+
properties: {key: 'markerLayer'},
62+
zIndex: 1,
63+
});
64+
65+
map.addLayer(newMarkerLayer);
66+
return newMarkerLayer;
67+
}
68+
3169
export function Marker({
3270
lonLat=undefined,
3371
address=undefined,
@@ -39,29 +77,11 @@ export function Marker({
3977
const map = useMap();
4078
let markerChar = char;
4179

42-
function addMarker(layer, lonLat, color, char) {
43-
const markerFeature = new ol.Feature({
44-
geometry: new Point(fromLonLat(lonLat))
45-
});
46-
markerFeature.setStyle(new Style({
47-
image: new Icon({
48-
scale: 1,
49-
anchor: [0.5,40],
50-
anchorXUnits: 'fraction',
51-
anchorYUnits: 'pixels',
52-
src: getMarkerImage(color, char)
53-
}),
54-
}));
55-
56-
layer.getSource().addFeature(markerFeature);
57-
return markerFeature;
58-
}
59-
6080
useEffect(() => {
6181
if (!map) return;
6282

63-
const markerLayer = map.getLayers().getArray()
64-
.find(el => el.get('key') === 'markerLayer') as VectorLayer;
83+
const markerLayer = getMarkerLayer(map);
84+
6585
if (lonLat) {
6686
addMarker(markerLayer, lonLat, color, markerChar);
6787
map.getView().setCenter(fromLonLat(lonLat))

src/lib/layers/LayerGroup.tsx

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import * as ol from 'ol';
2+
import { createContext, useContext, useEffect, useRef } from 'react';
3+
import olLayerGroup from 'ol/layer/Group';
4+
import { useMap } from '../Map';
5+
6+
const LayerGroupContext = createContext<olLayerGroup | undefined>(undefined);
7+
8+
export const useGroup = () => useContext(LayerGroupContext);
9+
10+
export const LayerGroup = (props: any) => {
11+
const map = useMap();
12+
const parentGroup = useContext(LayerGroupContext); // Check for a parent LayerGroup
13+
const layerGroupRef = useRef(new olLayerGroup(props));
14+
15+
useEffect(() => {
16+
const layerGroup = layerGroupRef.current;
17+
const target = parentGroup || map; // Prefer parent LayerGroup, else Map
18+
19+
if (target) {
20+
if (target instanceof ol.Map) {
21+
target.addLayer(layerGroup);
22+
} else {
23+
target.getLayers().push(layerGroup); // Add to parent LayerGroup
24+
}
25+
}
26+
27+
return () => {
28+
if (target) {
29+
if (target instanceof ol.Map) {
30+
target.removeLayer(layerGroup);
31+
} else {
32+
target.getLayers().remove(layerGroup);
33+
}
34+
}
35+
};
36+
}, [map, parentGroup]);
37+
38+
return (
39+
<LayerGroupContext.Provider value={layerGroupRef.current}>
40+
{props.children}
41+
</LayerGroupContext.Provider>
42+
);
43+
};

src/lib/layers/TileLayer.tsx

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,38 @@
1-
import { useEffect } from 'react';
1+
import * as ol from 'ol';
2+
import { useEffect, useRef } from 'react';
23
import OlTileLayer from 'ol/layer/Tile';
34
import { useMap } from '../Map';
5+
import { useGroup } from './LayerGroup';
46

5-
export function TileLayer(props) {
7+
export function TileLayer(props: any) {
68
const map = useMap();
9+
const group = useGroup();
10+
const layerRef = useRef(new OlTileLayer(props)); // single instance
711

812
useEffect(() => {
9-
if (!map) return;
10-
const layer = new OlTileLayer(props);
11-
map.addLayer(layer);
12-
}, [map]);
13+
if (!map && !group) return;
14+
15+
const layer = layerRef.current; // same instance every time
16+
const target = group || map;
17+
18+
if (target) {
19+
if (target instanceof ol.Map) {
20+
target.addLayer(layer);
21+
} else {
22+
target.getLayers().push(layer);
23+
}
24+
}
25+
26+
return () => {
27+
if (target) {
28+
if (target instanceof ol.Map) {
29+
target.removeLayer(layer);
30+
} else {
31+
target.getLayers().remove(layer);
32+
}
33+
}
34+
};
35+
}, [map, group]);
1336

1437
return null;
15-
};
38+
}

src/stories/Map/Map.mdx

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -11,20 +11,8 @@ https://openlayers.org/en/latest/apidoc/module-ol_Map-Map.html
1111

1212
### Usage
1313
```typescript
14-
import * as ol from 'ol';
15-
import { OSM } from 'ol/source';
16-
import { Map, View, TileLayer } from 'react-openlayers';
17-
18-
export default function(props) {
19-
const mapRef= useRef<ol.Map>();
20-
setTimeout(() => console.log(olMap.current), 1000));
21-
22-
return (
23-
<Map ref={mapRef} controls={[]} interactions={[]}>
24-
<TileLayer source={new OSM()} />
25-
<View center={[-10997148, 4569099]} zoom={4}/>
26-
</Map>
27-
);
14+
export default function() {
15+
return <Map />;
2816
}
2917
```
3018

src/stories/Map/Map.stories.tsx

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,19 +7,7 @@ export default { title: 'Map' } as Meta;
77
declare let window:any;
88

99
export const Primary: StoryObj<any> = {
10-
render: (props) => {
11-
const mapRef = useRef();
12-
13-
setTimeout(() => {
14-
window.map=mapRef.current;
15-
console.log({map: window.map});
16-
}, 1000);
17-
18-
return (
19-
<Map ref={mapRef} controls={[]} interactions={[]}>
20-
<TileLayer source={new OSM()} />
21-
<View center={[0,0]} zoom={4}/>
22-
</Map>
23-
);
10+
render: () => {
11+
return <Map />;
2412
}
2513
}

src/stories/controls/AttributionControl/AttributionControl.mdx

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,12 @@ https://openlayers.org/en/latest/apidoc/module-ol_control_Attribution-Attributio
1515

1616
### Usage
1717
```typescript
18-
import OSM from 'ol/source/OSM';
1918
import { Map, TileLayer, View, AttributionControl } from 'react-openlayers';
2019

2120
export default function(props) {
22-
const mapRef = useRef();
2321
return (
24-
<Map ref={mapRef}>
22+
<Map controls={[]}>
2523
<AttributionControl attributions="Hello me"/>
26-
<TileLayer source={new OSM()} />
2724
<View center={[-10997148, 4569099]} zoom={4}/>
2825
</Map>
2926
)

src/stories/controls/AttributionControl/AttributionControl.stories.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,8 @@ export default {
88
export const Primary = {
99
render: (props) => {
1010
return (
11-
<Map>
11+
<Map controls={[]}>
1212
<AttributionControl collapsible={true} attributions='Hello Attribution' />
13-
<TileLayer source={new OSM()} />
1413
<View center={[-10997148, 4569099]} zoom={4}/>
1514
</Map>
1615
);

src/stories/controls/DrawControl/DrawControl.mdx

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,15 +22,12 @@ Press shift key for freehand drawing.
2222

2323
### Usage
2424
```typescript
25-
import OSM from 'ol/source/OSM';
2625
import { Map, TileLayer, View, DrawControl, SelectInteraction, ModifyInteraction } from 'react-openlayers';
2726

2827
export default function(props) {
29-
const mapRef = useRef();
3028
return (
31-
<Map ref={mapRef}>
29+
<Map controls={[]}>
3230
<DrawControl />
33-
<TileLayer source={new OSM()} />
3431
<View center={[-10997148, 4569099]} zoom={4}/>
3532
<SelectInteraction />
3633
<TranslateInteraction />

src/stories/controls/DrawControl/DrawControl.stories.tsx

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import OSM from 'ol/source/OSM';
21
import {Map, TileLayer, View, DrawControl, SelectInteraction, TranslateInteraction} from '../../../lib';
32

43
export default {
@@ -8,9 +7,8 @@ export default {
87
export const Primary = {
98
render: (props) => {
109
return (
11-
<Map>
10+
<Map controls={[]}>
1211
<DrawControl />
13-
<TileLayer source={new OSM()} />
1412
<View center={[-10997148, 4569099]} zoom={4}/>
1513
<SelectInteraction />
1614
<TranslateInteraction />

0 commit comments

Comments
 (0)