MapGL.js 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267
  1. import React from "react";
  2. import ReactMapGL from "react-map-gl";
  3. import {
  4. Marker,
  5. NavigationControl,
  6. FullscreenControl,
  7. ScaleControl,
  8. GeolocateControl,
  9. Layer,
  10. Source,
  11. } from "react-map-gl";
  12. import "./../css/maps.css";
  13. import mapboxToken from "./../config/mapboxToken.js";
  14. import {calcDistance} from "./../helpers/distance";
  15. import {longitude, latitude, mapWidth, mapHeight, markerIcon} from "./../config/variables";
  16. import {Button, Input, Label, CustomInput, Row, Col, Container} from "reactstrap";
  17. import {getRoute} from "./../helpers/mapboxHelper";
  18. import PathButtons from "./../PathButtons";
  19. import {
  20. Editor,
  21. EditingMode,
  22. DrawLineStringMode,
  23. DrawPolygonMode,
  24. } from "react-map-gl-draw";
  25. const drawModes = [
  26. {id: "drawPolyline", text: "Draw Polyline", handler: DrawLineStringMode},
  27. {id: "drawPolygon", text: "Draw Polygon", handler: DrawPolygonMode},
  28. {id: "editing", text: "Edit Feature", handler: EditingMode},
  29. ];
  30. export default class MapGL extends React.Component{
  31. constructor(props){
  32. super(props);
  33. this.state = {
  34. viewport: {
  35. width: mapWidth,
  36. height: mapHeight,
  37. latitude: latitude,
  38. longitude: longitude,
  39. zoom: 14,
  40. bearing: 0,
  41. pitch: 0
  42. },
  43. path: [[longitude, latitude]],
  44. distance: 0,
  45. drawModeId: null,
  46. drawModeHandler: null,
  47. color: "rgb(189,189,189)",
  48. strokeDash: "4,2",
  49. strokeWidth: 2,
  50. fill: "rgb(189,189,189)",
  51. fillOpacity: 0.2
  52. }
  53. this.getRoute = getRoute.bind(this)
  54. }
  55. updateState = (key, value) => {
  56. this.setState({[key]: value})
  57. this.getDistance()
  58. }
  59. addMarker = event => {
  60. if (this.state.drawModeHandler===null){
  61. const lng = event.lngLat[0]
  62. const lat = event.lngLat[1]
  63. this.getRoute(lng, lat);
  64. }
  65. }
  66. updateMarker = (idx, event) => {
  67. const lng = event.lngLat[0]
  68. const lat = event.lngLat[1]
  69. let pathCopy = this.state.path.slice();
  70. pathCopy[idx] = [lng, lat]
  71. this.setState({path: pathCopy})
  72. this.getDistance()
  73. }
  74. getDistance = () => {
  75. let dst = calcDistance(this.state.path)
  76. this.setState({distance: dst})
  77. }
  78. clearPath = () => {
  79. this.setState({path: [], distance: 0})
  80. }
  81. switchDrawMode = (event) => {
  82. const modeId = event.target.value===this.state.drawModeId ? null:event.target.value;
  83. const mode = drawModes.find((m) => m.id===modeId);
  84. const modeHandler = mode ? new mode.handler() : null;
  85. this.setState({"drawModeid": modeId, "drawModeHandler": modeHandler});
  86. };
  87. drawToolbar = () => {
  88. return (
  89. <div style={{maxWidth: "320px"}}>
  90. <Input type="select" onChange={this.switchDrawMode}>
  91. <option value="">-</option>
  92. {drawModes.map((mode) => (
  93. <option key={mode.id} value={mode.id}>
  94. {mode.text}
  95. </option>
  96. ))}
  97. </Input>
  98. </div>
  99. )
  100. }
  101. render() {
  102. let testMarkers = this.props.markers
  103. let markers = <></>
  104. if (testMarkers){
  105. markers = testMarkers.map((pos, idx) => {
  106. return(
  107. <Marker
  108. longitude={pos[0]}
  109. latitude={pos[1]}
  110. captureClick={false}
  111. offsetTop={-20}
  112. offsetLeft={-8}
  113. key={idx}
  114. >
  115. <svg height={20} viewBox="0 0 24 24" style={{fill: "#343a40", stroke: "none", position: "absolute", top: "3px"}}>
  116. <path d={markerIcon} />
  117. </svg>
  118. </Marker>
  119. )}
  120. )
  121. }else{
  122. markers = this.state.path.map((pos, idx) => {
  123. return(
  124. <Marker
  125. longitude={pos[0]}
  126. latitude={pos[1]}
  127. captureClick={false}
  128. draggable={true}
  129. offsetTop={-20}
  130. offsetLeft={-8}
  131. key={idx}
  132. onDragEnd={(e) => this.updateMarker(idx, e)}
  133. >
  134. <svg height={20} viewBox="0 0 24 24" style={{fill: "#343a40", stroke: "none", position: "absolute", top: "3px"}}>
  135. {(idx===0 || idx===this.state.path.length-1 ?
  136. (<path d={markerIcon} /> )
  137. : (<circle cx="10" cy="19" r="5"/>)
  138. )}
  139. </svg>
  140. </Marker>
  141. )
  142. })
  143. }
  144. return (
  145. <div>
  146. <div className="map">
  147. <ReactMapGL
  148. {...this.state.viewport}
  149. onViewportChange={(viewport) => this.setState({viewport})}
  150. mapboxApiAccessToken={mapboxToken}
  151. mapStyle="mapbox://styles/mapbox/streets-v11"
  152. onClick={this.addMarker}
  153. >
  154. <Source
  155. id="routeSource"
  156. type="geojson"
  157. data={{type: "LineString", coordinates: this.state.path}}
  158. >
  159. <Layer
  160. source="routeSource"
  161. id="route"
  162. type="line"
  163. paint={{"line-color": "#888", "line-width": 3}}
  164. layout= {{"line-join": "round", "line-cap": "round"}}
  165. />
  166. </Source>
  167. {markers}
  168. <Editor
  169. clickRadius={12}
  170. mode={this.state.drawModeHandler}
  171. onSelect={(_) => {console.log("on select")}}
  172. featureStyle={({ feature, state }) => {
  173. return {
  174. stroke: this.state.color,
  175. strokeDasharray: this.state.strokeDash,
  176. strokeWidth: this.state.strokeWidth,
  177. fill: this.state.fill,
  178. fillOpacity: this.state.fillOpacity
  179. }
  180. }}
  181. />
  182. <div className="controllers">
  183. <GeolocateControl />
  184. <FullscreenControl />
  185. <NavigationControl />
  186. <ScaleControl />
  187. <small> {'\u00A9'} Mapbox {'\u00A9'} OpenStreetMap </small>
  188. </div>
  189. </ReactMapGL>
  190. </div>
  191. <div className="mapInfo">
  192. <Button onClick={this.clearPath}>Clear path</Button>
  193. <Button onClick={this.getDistance}>Calculate Distance</Button>
  194. <p>
  195. Distanz: {Math.round(this.state.distance*100)/100} km
  196. </p>
  197. <PathButtons updateState={this.updateState} path={this.state.path} switchLatLng={true}/>
  198. <br/>
  199. <div>
  200. <Container>
  201. <Row>
  202. <Col xs="4">
  203. {this.drawToolbar()}
  204. </Col>
  205. <Col xs="2">
  206. <Label for="colorPicker">Stroke Dash</Label>
  207. </Col>
  208. <Col xs="6">
  209. <Input type="text" onChange={(e) => this.setState({"strokeDash": e.target.value})} name="strokeDash" id="strokeDash" placeholder="4,2"/>
  210. </Col>
  211. </Row>
  212. <Row>
  213. <Col xs="2">
  214. <Label for="colorPicker">Line Color</Label>
  215. </Col>
  216. <Col xs="2">
  217. <Input type="color" onChange={(e) => this.setState({"color": e.target.value})} name="color" id="colorPicker" placeholder="colorPicker"/>
  218. </Col>
  219. <Col xs="2">
  220. <Label for="weight">Weight</Label>
  221. </Col>
  222. <Col xs="6">
  223. <Input type="range" onChange={(e) => this.setState({"strokeWidth": e.target.value})} name="weight" id="weight" placeholder="weight" min="1" max="20" value={this.state.weight}/>
  224. </Col>
  225. </Row>
  226. <Row>
  227. <Col xs="2">
  228. <Label for="fillColorPicker">Fill Color</Label>
  229. </Col>
  230. <Col xs="2">
  231. <Input type="color" onChange={(e) => this.setState({"fill": e.target.value})} name="fillColorPicker" id="fillColorPicker" placeholder="fillColorPicker"/>
  232. </Col>
  233. <Col xs="2">
  234. <Label for="fillOpacity">Fill Opacity</Label>
  235. </Col>
  236. <Col xs="6">
  237. <Input type="range" onChange={(e) => this.setState({"fillOpacity": e.target.value})} name="fillOpacity" id="fillOpacity" placeholder="fillOpacity" min="0" max="1" step="0.1" value={this.state.fillOpacity}/>
  238. </Col>
  239. </Row>
  240. </Container>
  241. </div>
  242. </div>
  243. </div>
  244. );
  245. }
  246. }