MapGL.js 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  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} 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. }
  48. this.getRoute = getRoute.bind(this)
  49. }
  50. updateState = (key, value) => {
  51. this.setState({[key]: value})
  52. this.getDistance()
  53. }
  54. addMarker = event => {
  55. const lng = event.lngLat[0]
  56. const lat = event.lngLat[1]
  57. this.getRoute(lng, lat);
  58. }
  59. updateMarker = (idx, event) => {
  60. const lng = event.lngLat[0]
  61. const lat = event.lngLat[1]
  62. let pathCopy = this.state.path.slice();
  63. pathCopy[idx] = [lng, lat]
  64. this.setState({path: pathCopy})
  65. this.getDistance()
  66. }
  67. getDistance = () => {
  68. let dst = calcDistance(this.state.path)
  69. this.setState({distance: dst})
  70. }
  71. clearPath = () => {
  72. this.setState({path: [], distance: 0})
  73. }
  74. switchDrawMode = (event) => {
  75. const modeId = event.target.value===this.state.drawModeId ? null:event.target.value;
  76. const mode = drawModes.find((m) => m.id===modeId);
  77. const modeHandler = mode ? new mode.handler() : null;
  78. this.setState({"drawModeid": modeId, "drawModeHandler": modeHandler });
  79. };
  80. drawToolbar = () => {
  81. return (
  82. <div style={{ position: "absolute", top: 0, right: 0, maxWidth: "320px"}}>
  83. <select onChange={this.switchDrawMode}>
  84. <option value="">--Please choose a draw mode--</option>
  85. {drawModes.map((mode) => (
  86. <option key={mode.id} value={mode.id}>
  87. {mode.text}
  88. </option>
  89. ))}
  90. </select>
  91. </div>
  92. )
  93. }
  94. render() {
  95. let testMarkers = this.props.markers
  96. let markers = <></>
  97. if (testMarkers){
  98. markers = testMarkers.map((pos, idx) => {
  99. return(
  100. <Marker
  101. longitude={pos[0]}
  102. latitude={pos[1]}
  103. captureClick={false}
  104. offsetTop={-20}
  105. offsetLeft={-8}
  106. key={idx}
  107. >
  108. <svg height={20} viewBox="0 0 24 24" style={{fill: "#343a40", stroke: "none", position: "absolute", top: "3px"}}>
  109. <path d={markerIcon} />
  110. </svg>
  111. </Marker>
  112. )}
  113. )
  114. }else{
  115. markers = this.state.path.map((pos, idx) => {
  116. return(
  117. <Marker
  118. longitude={pos[0]}
  119. latitude={pos[1]}
  120. captureClick={false}
  121. draggable={true}
  122. offsetTop={-20}
  123. offsetLeft={-8}
  124. key={idx}
  125. onDragEnd={(e) => this.updateMarker(idx, e)}
  126. >
  127. <svg height={20} viewBox="0 0 24 24" style={{fill: "#343a40", stroke: "none", position: "absolute", top: "3px"}}>
  128. {(idx===0 || idx===this.state.path.length-1 ?
  129. (<path d={markerIcon} /> )
  130. : (<circle cx="10" cy="19" r="5"/>)
  131. )}
  132. </svg>
  133. </Marker>
  134. )
  135. })
  136. }
  137. return (
  138. <div>
  139. <div className="map">
  140. <ReactMapGL
  141. {...this.state.viewport}
  142. onViewportChange={(viewport) => this.setState({viewport})}
  143. mapboxApiAccessToken={mapboxToken}
  144. mapStyle="mapbox://styles/mapbox/streets-v11"
  145. onClick={this.addMarker}
  146. >
  147. <Source
  148. id="routeSource"
  149. type="geojson"
  150. data={{type: "LineString", coordinates: this.state.path}}
  151. >
  152. <Layer
  153. source="routeSource"
  154. id="route"
  155. type="line"
  156. paint={{"line-color": "#888", "line-width": 3}}
  157. layout= {{"line-join": "round", "line-cap": "round"}}
  158. />
  159. </Source>
  160. {markers}
  161. <Editor
  162. clickRadius={12}
  163. mode={this.state.drawModeHandler}
  164. onSelect={(_) => {}}
  165. featureStyle={({ feature, state }) => {
  166. return {
  167. stroke: "rgb(189,189,189)",
  168. strokeDasharray: "4,2",
  169. strokeWidth: 2,
  170. fill: "rgb(189,189,189)",
  171. fillOpacity: 0.1
  172. }
  173. }}
  174. />
  175. {this.drawToolbar()}
  176. <div className="controllers">
  177. <GeolocateControl />
  178. <FullscreenControl />
  179. <NavigationControl />
  180. <ScaleControl />
  181. <small> {'\u00A9'} Mapbox {'\u00A9'} OpenStreetMap </small>
  182. </div>
  183. </ReactMapGL>
  184. </div>
  185. <div className="mapInfo">
  186. <Button onClick={this.clearPath}>Clear path</Button>
  187. <Button onClick={this.getDistance}>Calculate Distance</Button>
  188. <p>
  189. Distanz: {Math.round(this.state.distance*100)/100} km
  190. </p>
  191. <PathButtons updateState={this.updateState} path={this.state.path} switchLatLng={true}/>
  192. </div>
  193. </div>
  194. );
  195. }
  196. }