React js delete point from the path update the Polygon - javascript

I have the following map, as you should on the map I have a polygon and markers which are the main points of the polygon, below I have the main points of the polygon printed, i.e. the markers.
I give the user the possibility to delete points of the polygon, as you can see below in the image by clicking on the X, the point is eliminated.
The problem is this, when I click on the X, the point is eliminated as a marker, but it seems to remain as a fixed point of the polygon, which it really shouldn't do, the polygon should change its shape, based on the eliminated point.
I am not able to understand where I am wrong.
Can you give me some help.
Link: codesandbox
Index:
import React from "react";
import ReactDOM from "react-dom";
import Map from "./Map";
import "./styles.css";
//import { makeStyles } from "#material-ui/core/styles";
import ExpansionPanel from "#material-ui/core/ExpansionPanel";
import ExpansionPanelSummary from "#material-ui/core/ExpansionPanelSummary";
import ExpansionPanelDetails from "#material-ui/core/ExpansionPanelDetails";
import Typography from "#material-ui/core/Typography";
import ExpandMoreIcon from "#material-ui/icons/ExpandMore";
import ClearIcon from "#material-ui/icons/Clear";
import TextField from "#material-ui/core/TextField";
const API_KEY = "MY_API_KEY";
/*const useStyles = makeStyles(theme => ({
root: {
width: "100%"
},
heading: {
fontSize: theme.typography.pxToRem(15),
fontWeight: theme.typography.fontWeightRegular
}
}));*/
const useStyles = theme => ({
root: {
width: "100%"
},
heading: {
fontSize: theme.typography.pxToRem(15),
fontWeight: theme.typography.fontWeightRegular
}
});
const center = {
lat: 38.9065495,
lng: -77.0518192
};
//const classes = useStyles();
//className={classes.heading}
export default class App extends React.Component {
constructor(props) {
super(props);
this.state = {
paths: [
{ lat: 38.97330905858943, lng: -77.10469090410157 },
{ lat: 38.9209748864926, lng: -76.9083102888672 },
{ lat: 38.82689001319151, lng: -76.92204319902345 },
{ lat: 38.82261046915962, lng: -77.0181735701172 },
{ lat: 38.90174038629909, lng: -77.14314305253907 }
]
};
}
render() {
const { paths } = this.state;
return (
<div className="App2">
<Map
apiKey={API_KEY}
center={center}
paths={paths}
point={paths => this.setState({ paths })}
/>
{paths.map((pos, key) => {
return (
<ExpansionPanel key={key}>
<ExpansionPanelSummary
expandIcon={<ExpandMoreIcon />}
aria-controls="panel1a-content"
id="panel1a-header"
>
<ClearIcon
style={{ color: "#dc004e" }}
onClick={() => {
paths.splice(key, 1);
console.log(paths);
this.setState({ paths: paths });
}}
/>
<Typography>Point #{key}</Typography>
</ExpansionPanelSummary>
<ExpansionPanelDetails>
<TextField
fullWidth
key={"lat" + key}
label="Latitude"
type="text"
value={pos.lat}
disabled={true}
/>
<TextField
fullWidth
key={"lng" + key}
label="Longitude"
type="text"
value={pos.lng}
disabled={true}
/>
</ExpansionPanelDetails>
</ExpansionPanel>
);
})}
</div>
);
}
}
//export default withStyles(useStyles)(App);
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
Map:
import React, { useState, useRef, useCallback } from "react";
import {
LoadScript,
GoogleMap,
DrawingManager,
Polygon,
Marker
} from "#react-google-maps/api";
import "./styles.css";
const libraries = ["drawing"];
const options = {
drawingControl: true,
drawingControlOptions: {
drawingModes: ["polygon"]
},
polygonOptions: {
fillColor: `#2196F3`,
strokeColor: `#2196F3`,
fillOpacity: 0.5,
strokeWeight: 2,
clickable: true,
editable: true,
draggable: true,
zIndex: 1
}
};
class LoadScriptOnlyIfNeeded extends LoadScript {
componentDidMount() {
const cleaningUp = true;
const isBrowser = typeof document !== "undefined"; // require('#react-google-maps/api/src/utils/isbrowser')
const isAlreadyLoaded =
window.google &&
window.google.maps &&
document.querySelector("body.first-hit-completed"); // AJAX page loading system is adding this class the first time the app is loaded
if (!isAlreadyLoaded && isBrowser) {
// #ts-ignore
if (window.google && !cleaningUp) {
console.error("google api is already presented");
return;
}
this.isCleaningUp().then(this.injectScript);
}
if (isAlreadyLoaded) {
this.setState({ loaded: true });
}
}
}
export default function Map({ apiKey, center, paths = [], point }) {
const [path, setPath] = useState(paths);
const [state, setState] = useState({
drawingMode: "polygon"
});
const noDraw = () => {
setState(function set(prevState) {
return Object.assign({}, prevState, {
drawingMode: "maker"
});
});
};
const onPolygonComplete = React.useCallback(
function onPolygonComplete(poly) {
const polyArray = poly.getPath().getArray();
let paths = [];
polyArray.forEach(function(path) {
paths.push({ lat: path.lat(), lng: path.lng() });
});
setPath(paths);
console.log("onPolygonComplete", paths);
point(paths);
noDraw();
poly.setMap(null);
},
[point]
);
/*const onLoad = React.useCallback(function onLoad(map) {
//console.log(map);
}, []);
const onDrawingManagerLoad = React.useCallback(function onDrawingManagerLoad(
drawingManager
) {
// console.log(drawingManager);
},
[]);*/
// Define refs for Polygon instance and listeners
const polygonRef = useRef(null);
const listenersRef = useRef([]);
// Call setPath with new edited path
const onEdit = useCallback(() => {
if (polygonRef.current) {
const nextPath = polygonRef.current
.getPath()
.getArray()
.map(latLng => {
return { lat: latLng.lat(), lng: latLng.lng() };
});
setPath(nextPath);
point(nextPath);
}
}, [setPath, point]);
// Bind refs to current Polygon and listeners
const onLoad = useCallback(
polygon => {
polygonRef.current = polygon;
const path = polygon.getPath();
listenersRef.current.push(
path.addListener("set_at", onEdit),
path.addListener("insert_at", onEdit),
path.addListener("remove_at", onEdit)
);
},
[onEdit]
);
// Clean up refs
const onUnmount = useCallback(() => {
listenersRef.current.forEach(lis => lis.remove());
polygonRef.current = null;
}, []);
console.log(path);
return (
<div className="App">
<LoadScriptOnlyIfNeeded
id="script-loader"
googleMapsApiKey={apiKey}
libraries={libraries}
language="it"
region="us"
>
<GoogleMap
mapContainerClassName="App-map"
center={center}
zoom={10}
version="weekly"
//onLoad={onLoad}
>
{path.length === 0 ? (
<DrawingManager
drawingMode={state.drawingMode}
options={options}
onPolygonComplete={onPolygonComplete}
//onLoad={onDrawingManagerLoad}
editable
draggable
// Event used when manipulating and adding points
onMouseUp={onEdit}
// Event used when dragging the whole Polygon
onDragEnd={onEdit}
/>
) : (
<Polygon
options={{
fillColor: `#2196F3`,
strokeColor: `#2196F3`,
fillOpacity: 0.5,
strokeWeight: 2
}}
// Make the Polygon editable / draggable
editable
draggable
path={path}
// Event used when manipulating and adding points
onMouseUp={onEdit}
// Event used when dragging the whole Polygon
onDragEnd={onEdit}
onLoad={onLoad}
onUnmount={onUnmount}
/>
)}
{path.map((pos, key) => {
return <Marker key={key} label={"" + key} position={pos} />;
})}
</GoogleMap>
</LoadScriptOnlyIfNeeded>
</div>
);
}

Add useEffect to Map component to update paths for each render
export default function Map({ apiKey, center, paths = [], point }) {
const [path, setPath] = useState();
const [state, setState] = useState({
drawingMode: "polygon"
});
useEffect(() => {
setPath(paths);
}, [paths]);
.
.
.
and use filter instead of splice
In React you should never mutate the state directly.
onClick={() => {
this.setState({
paths: this.state.paths.filter((_, i) => i !== key)
});
or use splice like below
onClick={() => {
const paths = this.state.paths;
this.setState({
paths: [...paths.slice(0,key), ...paths.slice(key+1)]})
}}
codesandbox

During the Cancel Event, Call the function with the index of that array in the function parameter .
removetheArray = value => {
const { paths } = this.state;
this.setState({
paths: paths.splice(value, 1)
});
};
Function name is the removetheArray and pass the index as the value, the array as removed and map is updated incase map is not updated you have to init the map.

Related

How to trigger function on button onClick in the InfoWindow content in react-google-maps/api?

I am trying to trigger the function reportPost() on the button onClick which is located in the infoWindow element. When I click on the marker icon and infoWindow starts to render, the function triggers itself and when the window is already loaded, the button does not work (can't trigger func). How can I prevent this and fix the button?
import { useState, useEffect, useCallback } from "react";
import {
GoogleMap,
useJsApiLoader,
Marker,
InfoWindow,
} from "#react-google-maps/api";
import Axios from "axios";
import markerIcon from "../images/markerIcon.png";
const containerStyle = {
width: "100%",
height: "100%",
};
const options = {
mapId: process.env.MAP_ID,
streetViewControl: false,
};
const center = {
lat: 52.22611704066942,
lng: 19.357910156250004,
};
function Map() {
const [markers, setMarkers] = useState([]);
useEffect(() => {
Axios.get("http://localhost:3001/api/markers").then((response) => {
setMarkers(response.data);
});
}, []);
function reportPost(markerid) {
console.log(markerid);
}
const { isLoaded } = useJsApiLoader({
id: "google-map-script",
googleMapsApiKey: process.env.REACT_APP_GOOGLE_MAPS_API_KEY,
});
const onLoad = useCallback(function callback(map) {
setMap(map);
}, []);
const onUnmount = useCallback(function callback(map) {
setMap(null);
}, []);
const [map, setMap] = useState(null);
const [activeMarker, setActiveMarker] = useState(null);
const handleActiveMarker = (marker) => {
setActiveMarker(marker);
};
return isLoaded ? (
<div className="w-100 h-hero flex">
<GoogleMap
mapContainerStyle={containerStyle}
options={options}
center={center}
zoom={6}
onLoad={onLoad}
onUnmount={onUnmount}
>
{markers.map(
(
{ markerid, latitude, longitude, description },
i,
arr
) => {
const position = {
lat: latitude,
lng: longitude,
};
return (
<Marker
key={markerid}
icon={markerIcon}
position={position}
onClick={() => handleActiveMarker(markerid)}
>
{activeMarker === markerid ? (
<InfoWindow
onCloseClick={() => setActiveMarker(null)}
>
<div>
<div>
{description}
</div>
<div>
<button
onClick={reportPost(markerid)}
>
Report Post
</button>
</div>
</div>
</InfoWindow>
) : null}
</Marker>
);
}
)}
</GoogleMap>
</div>
) : null;
}
export default Map;
I think the onclick needs a callback, you can try doing it this way:
<button onClick={() => reportPost(markerid)}>
Report Post
</button>

Unable to smoothly animate the icons on map when coordinates changes using mapbox-gl

this is my react map component:
import 'mapbox-gl/dist/mapbox-gl.css';
import './switcher/switcher.css';
import mapboxgl from 'mapbox-gl';
import React, { useRef, useLayoutEffect, useEffect, useState } from 'react';
import { deviceCategories } from '../common/deviceCategories';
import { loadIcon, loadImage } from './mapUtil';
import { styleCarto} from './mapStyles';
import { useAttributePreference } from '../common/preferences';
const element = document.createElement('div');
element.style.width = '100%';
element.style.height = '100%';
export const map = new mapboxgl.Map({
container: element,
style: styleCarto(),
center: [80.379370, 23.846870],
zoom: 4.8
});
let ready = false;
const readyListeners = new Set();
const addReadyListener = listener => {
readyListeners.add(listener);
listener(ready);
};
const removeReadyListener = listener => {
readyListeners.delete(listener);
};
const updateReadyValue = value => {
ready = value;
readyListeners.forEach(listener => listener(value));
};
const initMap = async () => {
const background = await loadImage('images/background.svg');
await Promise.all(deviceCategories.map(async category => {
if (!map.hasImage(category)) {
const imageData = await loadIcon(category, background, `images/icon/car.png`);
map.addImage(category, imageData, { pixelRatio: window.devicePixelRatio });
}
}));
updateReadyValue(true);
};
map.on('load', initMap);
map.addControl(new mapboxgl.NavigationControl({
showCompass: false,
}));
const Map = ({ children }) => {
const containerEl = useRef(null);
const [mapReady, setMapReady] = useState(false);
const mapboxAccessToken = useAttributePreference('mapboxAccessToken');
useEffect(() => {
mapboxgl.accessToken = mapboxAccessToken;
}, [mapboxAccessToken]);
useEffect(() => {
const listener = ready => setMapReady(ready);
addReadyListener(listener);
return () => {
removeReadyListener(listener);
};
}, []);
useLayoutEffect(() => {
const currentEl = containerEl.current;
currentEl.appendChild(element);
if (map) {
map.resize();
}
return () => {
currentEl.removeChild(element);
};
}, [containerEl]);
return (
<div style={{ width: '100%', height: '100%' }} ref={containerEl}>
{mapReady && children}
</div>
);
};
export default Map;
I am fetching coordinates from api endpoint using socket controller, there is redux store that handle the changes in the data, however the position of the icons changes but its not smooth ,i have been trying to make it done since 5 days but i dont find any way how to do it, i am not finding mapbox documentation helpful
Below is the position map component , here the positions are being refreshed and updated to new cordinates, but i want to animate the changes on screen like movement of car on uber/ola app.
import React, { useCallback, useEffect } from 'react';
import ReactDOM from 'react-dom';
import mapboxgl from 'mapbox-gl';
import { Provider, useSelector } from 'react-redux';
import { map } from './Map';
import store from '../store';
import { useHistory } from 'react-router-dom';
import StatusView from './StatusView';
const PositionsMap = ({ positions }) => {
const id = 'positions';
const history = useHistory();
const devices = useSelector(state => state.devices.items);
const createFeature = (devices, position) => {
const device = devices[position.deviceId] || null;
return {
deviceId: position.deviceId,
name: device ? device.name : '',
category: device && (device.category || 'default'),
}
};
const onMouseEnter = () => map.getCanvas().style.cursor = 'pointer';
const onMouseLeave = () => map.getCanvas().style.cursor = '';
const onClickCallback = useCallback(event => {
const feature = event.features[0];
let coordinates = feature.geometry.coordinates.slice();
while (Math.abs(event.lngLat.lng - coordinates[0]) > 180) {
coordinates[0] += event.lngLat.lng > coordinates[0] ? 360 : -360;
}
const placeholder = document.createElement('div');
ReactDOM.render(
<Provider store={store}>
<StatusView deviceId={feature.properties.deviceId} onShowDetails={positionId => history.push(`/position/${positionId}`)} />
</Provider>,
placeholder
);
new mapboxgl.Popup({
offset: 20,
anchor: 'bottom-left',
closeButton: false,
className: 'popup'
})
.setDOMContent(placeholder)
.setLngLat(coordinates)
.addTo(map);
}, [history]);
useEffect(() => {
map.addSource(id, {
'type': 'geojson',
'data': {
type: 'FeatureCollection',
features: [],
}
});
map.addLayer({
'id': id,
'type': 'symbol',
'source': id,
'layout': {
'icon-image': '{category}',
'icon-allow-overlap': true,
'text-field': '{name}',
'text-allow-overlap': true,
'text-anchor': 'bottom',
'text-offset': [0, -2],
'text-font': ['Roboto Regular'],
'text-size': 12,
}
});
map.on('mouseenter', id, onMouseEnter);
map.on('mouseleave', id, onMouseLeave);
map.on('click', id, onClickCallback);
return () => {
Array.from(map.getContainer().getElementsByClassName('mapboxgl-popup')).forEach(el => el.remove());
map.off('mouseenter', id, onMouseEnter);
map.off('mouseleave', id, onMouseLeave);
map.off('click', id, onClickCallback);
map.removeLayer(id);
map.removeSource(id);
};
}, [onClickCallback]);
useEffect(() => {
map.getSource(id).setData({
type: 'FeatureCollection',
features: positions.map(position => ({
type: 'Feature',
geometry: {
type: 'Point',
coordinates: [position.longitude, position.latitude]
},
properties: createFeature(devices, position),
}))
});
}, [devices, positions]);
return null;
}
export default PositionsMap;
can any body help on thin to figure what i have been missing

#react-google-maps/api "google is not defined " error when accessed inside UseEffect hook

what im trying to do
place a marker on the map and calculate the distance between the marker and a static location on the map.
if the distance is greater than 1000 metres then i display address not available modal
problem description
Unable to access google inside UseEffect .
I loaded the script properly and followed the documentation
window.google is available in the onMapClick function but not anywhere in my index component or inside the useEffect hook
How do i access it ? Everything else works fine
Unhandled Runtime Error
ReferenceError: google is not defined
import React, { useRef, useState, useEffect, useCallback } from 'react';
import {
GoogleMap,
useLoadScript,
Marker,
InfoWindow,
MarkerClusterer,
} from '#react-google-maps/api';
import '#reach/combobox/styles.css';
import usePlacesAutoComplete, {
getGeocode,
getLatLng,
} from 'use-places-autocomplete';
import getDistanceFromLatLonInKm from '../../utils/getDistanceFromLatLonInKm.js';
import Modal from 'react-bootstrap/Modal';
import Button from 'react-bootstrap/Button';
import { Circle } from '#react-google-maps/api';
const libraries = ['places', 'geometry'];
const mapContainerStyle = {
width: '100%',
height: '40vh',
};
const center = {
lat: 25.33800452203996,
lng: 55.393221974372864,
};
const options = {
zoomControl: true,
};
const circleOptions = {
strokeColor: '#00a3a6',
strokeOpacity: 0.4,
strokeWeight: 2,
fillColor: '#00a3a6',
fillOpacity: 0.1,
clickable: false,
draggable: false,
editable: false,
visible: true,
radius: 1050,
zIndex: 1,
};
const initialMarker = { lat: null, long: null };
export default function index() {
const { isLoaded, loadError } = useLoadScript({
googleMapsApiKey: process.env.NEXT_PUBLIC_GOOGLE_MAPS_API_KEY,
libraries,
});
const [marker, setMarker] = useState(initialMarker);
const [showModal, setShowModal] = useState(false);
const handleModalClose = () => {
setShowModal(false);
setMarker(initialMarker);
};
const onMapClick = (event) => {
setMarker({
lat: event.latLng.lat(),
lng: event.latLng.lng(),
});
console.log(window.google); //accessible here
};
const renderMap = () => {
return (
<GoogleMap
mapContainerStyle={mapContainerStyle}
zoom={14}
options={options}
center={center}
onClick={onMapClick}>
<Circle center={center} options={circleOptions} />
{marker && <Marker position={{ lat: marker.lat, lng: marker.lng }} />}
</GoogleMap>
);
};
useEffect(() => {
if (
//cant access google here
google.maps.geometry.spherical.computeDistanceBetween(
new google.maps.LatLng(center.lat, center.lng),
new google.maps.LatLng(marker.lat, marker.lng)
) > 1000
) {
setShowModal(true);
}
}, [marker.lat]);
if (loadError) return 'Error Loading Maps';
if (!isLoaded) return 'Loading Maps';
return (
<>
<div>{renderMap()}</div>
<Modal
show={showModal}
onHide={handleModalClose}
backdrop="static"
keyboard={false}
centered>
<Modal.Header closeButton>
<Modal.Title>Address is out of bounds</Modal.Title>
</Modal.Header>
<Modal.Body>Sorry ! We Dont Deliver Food In Your Area .</Modal.Body>
<Modal.Footer>
<Button onClick={handleModalClose}>Choose New Address</Button>
</Modal.Footer>
</Modal>
</>
);
}
Turn on the internet connection in your computer...it will definitely work

How can I drag and drop objects into a Mapbox map from outside the map?

How can I create elements outside of a Mapbox map object that can be dragged into it? For example, let's say I want to render a list of locations on a page. Each location is a React component with a custom marker or icon.
Next to this list of locations is a Mapbox map. The list of locations is not rendered inside the map. While I know it's possible to make these individual location components draggable, is it possible to drag and drop them into a Mapbox map and have it recognized as actual markers with latitude/longitude coordinates on the map? If so, how can I do this?
Here are the relevant source files in my code that I have tried:
index.js
import dynamic from "next/dynamic";
import { useSelector } from "react-redux";
import Plant from "../../components/Plant";
const MapboxMap = dynamic(() => import("../../components/MapboxGLMap"), {
ssr: false,
});
const Blueprint = () => {
const plants = useSelector((state) => state.plants);
const showPlants = () => {
return (
<React.Fragment>
{plants.map((plant) => (
<Plant plant={plant} />
))}
</React.Fragment>
);
};
return (
<React.Fragment>
<div className="ui container centered grid blueprint">
<div className="three wide column scrollable">
<div className="ui link one cards">{showPlants()}</div>
</div>
<div className="twelve wide column">
<MapboxMap />
</div>
<style jsx>{`
.scrollable {
height: calc(100vh);
overflow-x: auto;
}
`}</style>
</React.Fragment>
);
};
export default Blueprint;
Plant.jsx
import React from "react";
import { useDrag } from "react-dnd";
const ItemTypes = {
PLANT: "plant",
};
const Plant = ({ plant }) => {
const [{ isDragging }, drag] = useDrag({
item: { type: ItemTypes.PLANT },
collect: (monitor) => ({
isDragging: !!monitor.isDragging(),
}),
});
return (
<div
ref={drag}
style={{
opacity: isDragging ? 0.1 : 1,
cursor: "move",
}}
key={plant.id}
className="card"
>
<div className="image">
<img src="/white-image.png" />
</div>
<div className="content">
<div className="center aligned">{plant.common_name}</div>
</div>
</div>
);
};
export default Plant;
MapboxGLMap.jsx
import React, { useEffect, useRef, useState } from "react";
import mapboxgl from "mapbox-gl";
import MapboxGeocoder from "#mapbox/mapbox-gl-geocoder";
import MapboxDraw from "#mapbox/mapbox-gl-draw";
const MAPBOX_TOKEN = "xxx";
const styles = {
width: "100%",
height: "100%",
position: "absolute",
};
const MapboxGLMap = () => {
const [map, setMap] = useState(null);
const [lng, setLng] = useState(null);
const [lat, setLat] = useState(null);
const [plant, setPlant] = useState(null);
const mapContainer = useRef(null);
useEffect(() => {
mapboxgl.accessToken = MAPBOX_TOKEN;
const initializeMap = ({ setMap, mapContainer }) => {
const map = new mapboxgl.Map({
container: mapContainer.current,
style: "mapbox://styles/mapbox/satellite-v9", // stylesheet location
center: [0, 0],
zoom: 5,
});
map.on("load", () => {
setMap(map);
map.resize();
});
map.on("click", (e) => {});
map.addControl(
new MapboxGeocoder({
accessToken: MAPBOX_TOKEN,
mapboxgl: mapboxgl,
})
);
const draw = new MapboxDraw({
displayControlsDefault: false,
controls: {
polygon: true,
trash: true,
},
});
map.addControl(draw);
map.on("draw.create", (e) => {
console.log("e =>", e);
console.log("draw.getAll()", draw.getAll());
});
map.on("mousemove", (e) => {
// console.log(e.point);
setLng(e.lngLat.wrap().lng);
setLat(e.lngLat.wrap().lat);
});
};
if (!map) initializeMap({ setMap, mapContainer });
}, [map]);
return <div ref={(el) => (mapContainer.current = el)} style={styles} />;
};
export default MapboxGLMap;
Actually, based on your related tags I imagine that you wanna drag and drop something like pin from outside to the map area. you use reactjs tags, it means you wanna do it by using ReactJS.
For this, you should install Mapbox by npm or yarn:
npm install mapbox-gl --save
or
yarn add mapbox-gl
Then you should wrap the Mapbox area in a drop zone. for this, you could use react-dropzone. install it by the following command:
npm install react-dropzone --save
or
yarn add react-dropzone
Add the following line to HTML template:
<link href='https://api.tiles.mapbox.com/mapbox-gl-js/v1.11.1/mapbox-gl.css' rel='stylesheet' />
Then use it like below:
import React from 'react';
import Dropzone from 'react-dropzone';
import mapboxgl from 'mapbox-gl';
mapboxgl.accessToken = 'MAPBOX_ACCESS_TOKEN';
class MapComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
lng: 5,
lat: 34,
zoom: 2,
};
}
componentDidMount() {
const map = new mapboxgl.Map({
container: this.mapContainer,
style: 'mapbox://styles/mapbox/streets-v11',
center: [this.state.lng, this.state.lat],
zoom: this.state.zoom,
});
map.on('move', () => {
this.setState({
lng: map.getCenter().lng.toFixed(4),
lat: map.getCenter().lat.toFixed(4),
zoom: map.getZoom().toFixed(2),
});
});
}
render() {
return (
<Dropzone>
{({ getRootProps, getInputProps }) => (
<section>
<div {...getRootProps()}>
<input {...getInputProps()} />
<div
ref={el => {
this.mapContainer = el;
}}
/>
</div>
</section>
)}
</Dropzone>
);
}
}
By using this method you can drop some images and got it and based on the drop position show it on the map.
Note: reduce the file type catching to image file type like jpg/png

Passing ID in component

I am working on an app, however I am stuck currently on how to approach this.
import React from "react"
import { Switch } from "react-router-dom"
import LandingPage from "./LandingPage/"
import Dashboard from "./pages/Dashboard"
import CustomRoute from "../utils/CustomRoute"
import Pages from "./pages/Pages"
import PublicTrip from "./Maps/singleTripPublic"
const Root = () => (
<Switch>
<CustomRoute path="/" exact component={LandingPage} />
<CustomRoute path="/app" protectedPath component={Dashboard} />
<CustomRoute
path="/public/:tripId"
render={({ match }) => <PublicTrip tripId={match.params.tripId} />}
/>
<CustomRoute path="/" component={Pages} />
<CustomRoute render={() => <div>404: Route not found</div>} />
<Pages />
</Switch>
)
export default Root
This is my root.js, I want to know how I can pass the tripId into Link tag so that it renders public/"tripId" when the link is clicked. If you scroll down you can see tag with tripId. How do I pass in tripId so that it actually redirects to it when I click. Any help would be appreciated. Thanks.
import React from "react"
import * as s from "./components"
import { connect } from "react-redux"
import moment from "moment"
import PropTypes from "prop-types"
import { TripPropTypes } from "../../propTypes"
import { Button } from "../../../styles/theme/styledComponents"
import { toggleWaypoint } from "../../../redux/actions/trips"
import marker from "../../icons/orange-marker.svg"
import startMarker from "../../icons/green-marker.svg"
import endMarker from "../../icons/black-marker.svg"
import { Link } from "react-router-dom"
class ActiveTripPanel extends React.Component {
constructor(props) {
super(props)
this.state = {
polylines: null,
markers: []
}
}
componentDidMount() {
setTimeout(() => {
this.renderWaypoints()
this.drawPolylines()
}, 500)
}
componentDidUpdate(prevProps) {
if (prevProps.waypoints !== this.props.waypoints) {
this.renderWaypoints()
this.drawPolylines()
}
}
drawPolylines = () => {
if (this.state.polylines !== null) {
this.state.polylines.active.setMap(null)
this.state.polylines.complete.setMap(null)
this.state.polylines.current.setMap(null)
}
let completeIndex = 0
for (let i = 0; i < this.props.waypoints.length; i++) {
if (!this.props.waypoints[i].complete) {
completeIndex = i
break
}
}
const completed = this.props.waypoints.slice(0, completeIndex)
const active = this.props.waypoints.slice(
completeIndex,
this.props.waypoints.length + 1
)
const current = this.props.waypoints.slice(
completeIndex - 1,
completeIndex + 2
)
const completePath = completed.map(waypoint => {
return { lat: waypoint.lat, lng: waypoint.lon }
})
const activePath = active.map(waypoint => {
return { lat: waypoint.lat, lng: waypoint.lon }
})
const currentPath = current.map(waypoint => {
return { lat: waypoint.lat, lng: waypoint.lon }
})
const completePolyline = new window.google.maps.Polyline({
path: completePath,
strokeColor: "#FF0000",
strokeOpacity: 1.0,
strokeWeight: 2
})
const currentPolyline = new window.google.maps.Polyline({
path: currentPath,
strokeColor: "#008000",
stokeOpacity: 1.0,
stokeWeight: 2
})
const activePolyline = new window.google.maps.Polyline({
path: activePath,
strokeColor: "#000000",
strokeOpacity: 1.0,
strokeWeight: 2
})
completePolyline.setMap(window.map)
activePolyline.setMap(window.map)
currentPolyline.setMap(window.map)
this.setState({
polylines: {
active: activePolyline,
complete: completePolyline,
current: currentPolyline
}
})
}
renderWaypoints = () => {
let markers = []
const baseIcon = {
anchor: new window.google.maps.Point(15, 30),
scaledSize: new window.google.maps.Size(30, 30),
labelOrigin: new window.google.maps.Point(15, 13)
}
const icons = {
start: {
url: startMarker,
...baseIcon
},
end: {
url: endMarker,
...baseIcon
},
marker: {
url: marker,
...baseIcon
}
}
this.props.waypoints.map((item, i) => {
const icon =
i === 0
? icons.start
: i === this.props.waypoints.length - 1
? icons.end
: icons.marker
let center = { lat: item.lat, lng: item.lon }
const marker = new window.google.maps.Marker({
position: center,
map: window.map,
icon,
title: item.name,
label: {
text: `${i + 1}`,
color: "white",
fontFamily: "Wals",
fontWeight: "bold"
}
})
markers.push(marker)
})
}
render() {
const publicId = ({ match })
return (
<s.Panel>
{/* <s.PanelHeader>{this.props.trip.name}</s.PanelHeader>
<s.DateLabel>
Start: {moment(this.props.trip.start).format("YYYY-MM-DD")} - End:{" "}
{moment(this.props.trip.end).format("YYYY-MM-DD")}
</s.DateLabel> */}
<Link to="/public/{match.params.tripId}">Share Trip</Link>
<s.WaypointTracker>
{this.props.waypoints &&
this.props.waypoints.map(waypoint => (
<s.WaypointStepper key={waypoint.id}>
<div>
<h4>{waypoint.name}</h4>
<div>
ETA: {moment(waypoint.start).format("YYYY-MM-DD HH:mm")}
</div>
<div>
Status: Checked In #{" "}
{moment(waypoint.start).format("HH:mm")}
</div>
</div>
<div>
{waypoint.complete ? (
<Button
onClick={() => this.props.toggleWaypoint(waypoint.id)}
>
<i className="fa fa-check" />
</Button>
) : (
<Button
onClick={() => this.props.toggleWaypoint(waypoint.id)}
>
<i className="fa fa-times" />
</Button>
)}
</div>
</s.WaypointStepper>
))}
</s.WaypointTracker>
</s.Panel>
)
}
}
ActiveTripPanel.propTypes = {
trip: TripPropTypes,
waypoints: PropTypes.array.isRequired,
toggleWaypoint: PropTypes.func.isRequired
}
const mapStateToProps = ({ trips }) => ({
trip: trips.activeTrip,
waypoints: trips.activeTrip && trips.activeTrip.waypoints
})
export default connect(
mapStateToProps,
{ toggleWaypoint }
)(ActiveTripPanel)
[EDIT]
CustomRoute code
import React from "react"
import { connect } from "react-redux"
import { Redirect, Route } from "react-router"
import { addTokenToState } from "../redux/actions/auth"
const CustomRoute = props => {
const { isLoggedIn, protectedPath, checkedForToken, ...rest } = props
// If not logged in and haven't checked for token yet,
// try to query DB for user with token:
if (!checkedForToken && !isLoggedIn) {
props.addTokenToState()
}
if (isLoggedIn || !protectedPath) {
return <Route {...rest} />
}
if (protectedPath && !isLoggedIn) {
return (
<Redirect
to={{
pathname: "/login",
state: { from: props.path }
}}
/>
)
}
}
const mapStateToProps = state => ({
isLoggedIn: state.auth.isLoggedIn,
checkedForToken: state.auth.checkedForToken
})
const mapDispatchToProps = { addTokenToState }
export default connect(
mapStateToProps,
mapDispatchToProps
)(CustomRoute)
I hope this helps
<Link to="/public/${match.params.tripId}">Share Trip</Link>
Change from this
<Link to="/public/{match.params.tripId}">Share Trip</Link>
To this
<Link to={`/public/${this.props.match.params.tripId}`}>Share Trip</Link>
Update
If you want to access match object in your Component you have to pass match like this
<CustomRoute
path="/public/:tripId"
render={({ match }) => <PublicTrip match={match} />}
// or this render={props => <PublicTrip {...props} />}
/>
in your code you just pass tripId so that mean you can't access match object, you can get tripId like this this.props.tripId
so your Link should be like this
<Link to={`/public/${this.props.tripId}`}>Share Trip</Link>
Actually you don't even to to use render to get match params, just simply as this
<CustomRoute path="/public/:tripId" component={PublicTrip} />
And Route will inject match to your ActiveTripPanel, so you can get match params in your ActiveTripPanel like this this.props.match.params

Categories