1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
|
import * as React from 'react';
import SvgMap from './SvgMap';
// Higher zoom level is more zoomed in
export enum ZoomLevel {
high,
medium,
low,
}
export enum MarkerStyle {
secure,
unsecure,
}
interface IProps {
center: [number, number]; // longitude, latitude
offset: [number, number]; // offset [x, y] from the center of the map
zoomLevel: ZoomLevel;
showMarker: boolean;
markerStyle: MarkerStyle;
className?: string;
}
interface IState {
bounds: {
width: number;
height: number;
};
}
export default class Map extends React.Component<IProps, IState> {
public state: IState = {
bounds: {
width: 0,
height: 0,
},
};
private containerRef = React.createRef<HTMLDivElement>();
public render() {
const { width, height } = this.state.bounds;
const readyToRenderTheMap = width > 0 && height > 0;
return (
<div className={this.props.className} ref={this.containerRef}>
{readyToRenderTheMap && (
<SvgMap
width={width}
height={height}
center={this.props.center}
offset={this.props.offset}
zoomLevel={this.zoomLevel(this.props.zoomLevel)}
showMarker={this.props.showMarker}
markerImagePath={this.markerImage(this.props.markerStyle)}
/>
)}
</div>
);
}
public componentDidMount() {
this.updateBounds();
}
public componentDidUpdate() {
this.updateBounds();
}
public shouldComponentUpdate(nextProps: IProps, nextState: IState) {
const oldProps = this.props;
const oldState = this.state;
return (
oldProps.center[0] !== nextProps.center[0] ||
oldProps.center[1] !== nextProps.center[1] ||
oldProps.offset[0] !== nextProps.offset[0] ||
oldProps.offset[1] !== nextProps.offset[1] ||
oldProps.zoomLevel !== nextProps.zoomLevel ||
oldProps.showMarker !== nextProps.showMarker ||
oldProps.markerStyle !== nextProps.markerStyle ||
oldState.bounds.width !== nextState.bounds.width ||
oldState.bounds.height !== nextState.bounds.height
);
}
private updateBounds() {
const containerRect = this.containerRef.current?.getBoundingClientRect();
if (containerRect) {
this.setState((state) => {
if (
containerRect.width === state.bounds.width &&
containerRect.height === state.bounds.height
) {
return null;
} else {
return {
bounds: {
width: containerRect.width,
height: containerRect.height,
},
};
}
});
}
}
// TODO: Remove zoom level in favor of center + coordinate span
// TODO: Zoomlevels below 2.22 makes australia invisible
private zoomLevel(variant: ZoomLevel) {
switch (variant) {
case ZoomLevel.low:
return 1;
case ZoomLevel.medium:
return 2.22;
case ZoomLevel.high:
return 5;
}
}
private markerImage(style: MarkerStyle): string {
switch (style) {
case MarkerStyle.secure:
return '../../assets/images/location-marker-secure.svg';
case MarkerStyle.unsecure:
return '../../assets/images/location-marker-unsecure.svg';
}
}
}
|