1 | import * as React from 'react';
|
2 | import { Map, GeoJSONSource, GeoJSONSourceRaw, Layer } from 'mapbox-gl';
|
3 | import { TilesJson } from './util/types';
|
4 | import { withMap } from './context';
|
5 |
|
6 | export interface Props {
|
7 | id: string;
|
8 | geoJsonSource?: GeoJSONSourceRaw;
|
9 | tileJsonSource?: TilesJson;
|
10 | map: Map;
|
11 | onSourceAdded?: (source: GeoJSONSource | TilesJson) => void;
|
12 | onSourceLoaded?: (source: GeoJSONSource | TilesJson) => void;
|
13 | }
|
14 |
|
15 | export type LayerWithBefore = Layer & { before?: string };
|
16 |
|
17 | export class Source extends React.Component<Props> {
|
18 | private id = this.props.id;
|
19 |
|
20 | private onStyleDataChange = () => {
|
21 |
|
22 |
|
23 | if (!this.props.map.getLayer(this.id)) {
|
24 | this.initialize();
|
25 | this.forceUpdate();
|
26 | }
|
27 | };
|
28 |
|
29 | public componentDidMount() {
|
30 | const { map } = this.props;
|
31 |
|
32 | map.on('styledata', this.onStyleDataChange);
|
33 | this.initialize();
|
34 | }
|
35 |
|
36 | private initialize = () => {
|
37 | const { map } = this.props;
|
38 | const { geoJsonSource, tileJsonSource, onSourceAdded } = this.props;
|
39 | if (!map.getSource(this.id) && (geoJsonSource || tileJsonSource)) {
|
40 | if (geoJsonSource) {
|
41 | map.addSource(this.id, geoJsonSource);
|
42 | } else if (tileJsonSource) {
|
43 | map.addSource(this.id, tileJsonSource);
|
44 | }
|
45 |
|
46 | map.on('sourcedata', this.onData);
|
47 |
|
48 | if (onSourceAdded) {
|
49 | onSourceAdded(map.getSource(this.id) as GeoJSONSource | TilesJson);
|
50 | }
|
51 | }
|
52 | };
|
53 |
|
54 | private onData = () => {
|
55 | const { map } = this.props;
|
56 |
|
57 | const source = map.getSource(this.props.id) as GeoJSONSource;
|
58 | if (!source || !map.isSourceLoaded(this.props.id)) {
|
59 | return;
|
60 | }
|
61 |
|
62 | const { onSourceLoaded } = this.props;
|
63 | if (source && onSourceLoaded) {
|
64 | onSourceLoaded(source);
|
65 | }
|
66 |
|
67 | if (source && this.props.geoJsonSource && this.props.geoJsonSource.data) {
|
68 | source.setData(this.props.geoJsonSource.data);
|
69 | }
|
70 | map.off('sourcedata', this.onData);
|
71 | };
|
72 |
|
73 | public removeSource(): LayerWithBefore[] {
|
74 | const { map } = this.props;
|
75 |
|
76 | if (map.getSource(this.id)) {
|
77 | let { layers = [] } = map.getStyle();
|
78 |
|
79 | layers = layers
|
80 | .map((layer, idx): LayerWithBefore => {
|
81 | const { id: before } = layers[idx + 1] || { id: undefined };
|
82 | return { ...layer, before };
|
83 | })
|
84 | .filter(layer => layer.source === this.id);
|
85 |
|
86 | layers.forEach(layer => map.removeLayer(layer.id));
|
87 |
|
88 | map.removeSource(this.id);
|
89 |
|
90 | return layers.reverse();
|
91 | }
|
92 |
|
93 | return [];
|
94 | }
|
95 |
|
96 | public componentWillUnmount() {
|
97 | const { map } = this.props;
|
98 |
|
99 | if (!map || !map.getStyle()) {
|
100 | return;
|
101 | }
|
102 |
|
103 | map.off('styledata', this.onStyleDataChange);
|
104 | this.removeSource();
|
105 | }
|
106 |
|
107 | public componentDidUpdate(prevProps: Props) {
|
108 | const { geoJsonSource, tileJsonSource, map } = prevProps;
|
109 | const source = map.getSource(this.id);
|
110 |
|
111 |
|
112 | if (tileJsonSource && this.props.tileJsonSource) {
|
113 | let urlUpdated = false;
|
114 | let tilesUpdated = false;
|
115 |
|
116 | if (source && source.type === 'vector') {
|
117 | const hasNewSourceUrl =
|
118 | tileJsonSource.url !== this.props.tileJsonSource.url;
|
119 |
|
120 | if (hasNewSourceUrl && this.props.tileJsonSource.url !== undefined) {
|
121 | source.setUrl(this.props.tileJsonSource.url);
|
122 | urlUpdated = true;
|
123 | }
|
124 |
|
125 | const hasNewSourceTiles =
|
126 | tileJsonSource.tiles !== this.props.tileJsonSource.tiles;
|
127 |
|
128 | if (
|
129 | hasNewSourceTiles &&
|
130 | this.props.tileJsonSource.tiles !== undefined
|
131 | ) {
|
132 | source.setTiles(this.props.tileJsonSource.tiles);
|
133 | tilesUpdated = true;
|
134 | }
|
135 | }
|
136 |
|
137 |
|
138 |
|
139 | const hasNewTilesSource =
|
140 | (!urlUpdated && tileJsonSource.url !== this.props.tileJsonSource.url) ||
|
141 |
|
142 | (!tilesUpdated &&
|
143 | tileJsonSource.tiles !== this.props.tileJsonSource.tiles) ||
|
144 | tileJsonSource.minzoom !== this.props.tileJsonSource.minzoom ||
|
145 | tileJsonSource.maxzoom !== this.props.tileJsonSource.maxzoom;
|
146 |
|
147 | if (hasNewTilesSource) {
|
148 | const layers = this.removeSource();
|
149 | map.addSource(this.id, this.props.tileJsonSource);
|
150 |
|
151 | layers.forEach(layer => map.addLayer(layer, layer.before));
|
152 | }
|
153 | }
|
154 |
|
155 |
|
156 | if (
|
157 | geoJsonSource &&
|
158 | this.props.geoJsonSource &&
|
159 | this.props.geoJsonSource.data !== geoJsonSource.data &&
|
160 | this.props.geoJsonSource.data &&
|
161 | source &&
|
162 | source.type === 'geojson'
|
163 | ) {
|
164 | source.setData(this.props.geoJsonSource.data);
|
165 | }
|
166 | }
|
167 |
|
168 | public render() {
|
169 | return null;
|
170 | }
|
171 | }
|
172 |
|
173 | export default withMap(Source);
|