UNPKG

4.96 kBPlain TextView Raw
1import * as React from 'react';
2import { Map, GeoJSONSource, GeoJSONSourceRaw, Layer } from 'mapbox-gl';
3import { TilesJson } from './util/types';
4import { withMap } from './context';
5
6export 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
15export type LayerWithBefore = Layer & { before?: string };
16
17export class Source extends React.Component<Props> {
18 private id = this.props.id;
19
20 private onStyleDataChange = () => {
21 // if the style of the map has been updated we won't have any sources anymore,
22 // add it back to the map and force re-rendering to redraw it
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 // Will fix datasource being empty
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 // Update tilesJsonSource
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 // Prefer the more targetted updates, but fallback to swapping out the entire source
138 // This applies to raster tile sources, for example
139 const hasNewTilesSource =
140 (!urlUpdated && tileJsonSource.url !== this.props.tileJsonSource.url) ||
141 // Check for reference equality on tiles array
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 // Update geoJsonSource data
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
173export default withMap(Source);