Mapbox geocoder in React - reactjs

I recently made my first app on React. I had a d3 map that looks a bit old now, so I decided to switch to Mapbox.
The data transmitted is a list of country names and a percentage. The goal is to visualize each country and its percentage.
I wrote all my code, and everything seemed to go well.
However, I now get this error on line 19 : "Geocoder is not defined".
I specify that I have installed :
mapbox-gl
react-mapbox-gl
react-map-gl-geocoder
Can someone explain to me where my mistake came from?
import React, { useRef, useEffect } from "react";
import mapboxgl from "mapbox-gl";
import MapGeocoder from "react-map-gl-geocoder";
const MapChart = ({ data }) => {
const mapContainer = useRef(null);
useEffect(() => {
mapboxgl.accessToken =
"**********";
const map = new mapboxgl.Map({
container: mapContainer.current,
style: "mapbox://styles/mapbox/streets-v12",
center: [0, 0],
zoom: 1
});
// Geocode the data points to get the latitude and longitude
const geocoder = new MapGeocoder({
accessToken: mapboxgl.accessToken
});
data.forEach((datapoint) => {
geocoder.geocode(datapoint.country, (error, result) => {
if (error) {
console.error(error);
return;
}
const [longitude, latitude] = result.features[0].geometry.coordinates;
const marker = new mapboxgl.Marker({
color: "#" + ((Math.random() * 0xffffff) << 0).toString(16)
}).setLngLat([longitude, latitude]);
marker.setPopup(
new mapboxgl.Popup().setHTML(`
<h3>${datapoint.country}</h3>
<p>${datapoint.percentage}%</p>
`)
);
marker.addTo(map);
});
});
}, [data]);
return <div ref={mapContainer} style={{ width: "100%", height: "400px" }} />;
};
export default MapChart;

You are actually using the MapboxGeocoder from '#mapbox/mapbox-gl-geocoder'. If you intend to use the one provided by react-map-gl-geocoder, it would work slightly differently.

Related

How to make dynamic changes in Arcgis JS map-component with React?

I am quite new to Arcgis-JS and React. As suggested here, I am using a functional component with the useEffect hook to integrate my map.
Now I want to display a line within my map when I click on a certain row of a list. On click I am fetching the appropriate coordinates to be displayed and storing them to a context-variable (dbPageContext.currentGeom).
The problem: When I want to display another line, the entire map-component has to re-render as I am passing the line-array-variable as a second argument to the useEffect hook.
const MapComp = () => {
const mapRef = useRef();
const dbPageContext = useContext(DbPageContext);
useEffect(() => {
const mainMap = new Map({
layers: [layer],
basemap: "arcgis-topographic", // Basemap layer service
});
const graphicsLayer = new GraphicsLayer();
mainMap.add(graphicsLayer);
const polyline = {
type: "polyline",
paths: dbPageContext.currentGeom,
};
const simpleLineSymbol = {
type: "simple-line",
color: [0, 230, 250],
width: 4,
};
const polylineGraphic = new Graphic({
geometry: polyline,
symbol: simpleLineSymbol,
});
graphicsLayer.add(polylineGraphic);
const view = new MapView({
container: mapRef.current,
map: mainMap,
spatialReference: {
wkid: 3857,
},
});
return () => {
view && view.destroy();
};
}, [dbPageContext.currentGeom]);
return (
<div>
<div className="webmap" ref={mapRef} />
</div>
);
};
export default MapComp;
How can I update only the graphics-layer without updating the entire map-component? Would be great if someone could help me finding a solution for that.
EDIT: I also tried to implement the map without using the useeffect hook. But then, nothing was displayed.
You need to separate the effects. On page load, you should have one effect that creates the map. Then a second effect can update the map when dbPageContext.currentGeom changes.
const MapComp = () => {
const mapRef = useRef();
const dbPageContext = useContext(DbPageContext);
// Memoize this, as you only need to create it once, but you also need
// it to be available within scope of both of the following useEffects
const graphicsLayer = React.useMemo(
() => new GraphicsLayer(),
[]
);
// On mount, create the map, view, and teardown
useEffect(() => {
const mainMap = new Map({
layers: [layer],
basemap: "arcgis-topographic", // Basemap layer service
});
const view = new MapView({
container: mapRef.current,
map: mainMap,
spatialReference: {
wkid: 3857,
},
});
mainMap.add(graphicsLayer);
return () => {
view && view.destroy();
};
}, [])
// When dbPageContext.currentGeom changes, add a polyline
// to the graphics layer
useEffect(() => {
const polyline = {
type: "polyline",
paths: dbPageContext.currentGeom,
};
const simpleLineSymbol = {
type: "simple-line",
color: [0, 230, 250],
width: 4,
};
const polylineGraphic = new Graphic({
geometry: polyline,
symbol: simpleLineSymbol,
});
// Clear previously added lines (if that's what you want)
graphicsLayer.removeAll()
graphicsLayer.add(polylineGraphic);
}, [dbPageContext.currentGeom]);
return (
<div>
<div className="webmap" ref={mapRef} />
</div>
);
};
export default MapComp;

AMCharts - map disappearing after data is set on MapPolygonSeries (React)

I hope someone can help with this one.
I'm using AMCharts packages as dependencies in the React app. Everything works till I set data on MapPolygonSeries (note: it works if all is set inside the same useLayoutEffect(), but that may work for basic setup but for further development is not an option).
Here is a code example.
import { useLayoutEffect, useRef, useState } from 'react';
import styled from 'styled-components';
import * as am5 from '#amcharts/amcharts5';
import * as am5map from '#amcharts/amcharts5/map';
import am5themes_Animated from '#amcharts/amcharts5/themes/Animated';
import am5geodata_worldLow from '#amcharts/amcharts5-geodata/worldLow';
const RootDiv = styled.div`
widht: 100%;
height: 500px;
`;
const AmChartsMapView = () => {
const worldSeriesRef = useRef(null);
const [worldData, setWorldData] = useState([]);
// for this example assume that data is correctly structured (e.g. [{id: 'countryId', value: 'some number'}] )
fetch('http://some-location-data.com/data.json')
.then((response) => response.json())
.then((data) => setWorldData(data));
useLayoutEffect(() => {
const root = am5.Root.new('rootdiv');
root.setThemes([am5themes_Animated.new(root)]);
const chart = am5map.MapChart.new(root, {
panX: 'translateX',
panY: 'translateY',
projection: am5map.geoMercator(),
});
root.container.children.push(chart);
const worldSeries = am5map.MapPolygonSeries.new(root, {
geoJSON: am5geodata_worldLow,
exclude: ['AQ'],
valueField: 'value',
calculateAggregates: true,
});
worldSeries.mapPolygons.template.setAll({
tooltipText: '{name}: {value}',
interactive: true,
fill: am5.color(0xaaaaaa),
templateField: 'polygonSettings',
});
chart.series.push(worldSeries);
worldSeriesRef.current = worldSeries;
return () => {
if (root) {
root.dispose();
rootRef.current = null;
}
}
},[]);
useLayoutEffect(() => {
if (!worldSeriesRef.current || !worldData.length) {
return;
}
//when this is added map disappear
worldSeriesRef.current.data.setAll(worldData);
}, [worldSeriesRef.current, worldData.length]);
return <RootDiv id="rootdiv" />;
}

Assigning ref in react-konva

How do we assign ref to the stage or layer object. I am using react-konva. When I do console.log(stageE1), it says undefined.
useEffect(() => {
var stage = new Konva.Stage({
container: 'stage',
width: window.innerWidth,
height: window.innerHeight,
ref: stageEl, // not working
onMouseDown: (e) => {
// deselect when clicked on empty area
const clickedOnEmpty = e.target === e.target.getStage()
if (clickedOnEmpty) {
selectShape(null)
}
},
})
stage.container().style.border = '1px solid grey'
var layer = new Konva.Layer({
ref: layerEl, // not working
})
stage.add(layer)
}, [])
This is not react-konva usage. You are using Konva API directly. If you do this, you probably don't need to use refs. But if you really want:
var layer = new Konva.Layer();
// set ref
layerEl.current = layer;
If you use react-konva, then you should define components in React way:
import { Stage, Layer } from 'react-konva';
const App = () => {
const layerEl = React.useRef();
return <Stage><Layer ref={layerEl} /></Stage>;
};

How to query routes in here maps with reactjs

I am trying to make map queries with here maps on routes with reactjs, currently I can display the map in my application with this code:
import * as React from 'react';
export const DisplayMapFC = () => {
// Create a reference to the HTML element we want to put the map on
const mapRef = React.useRef(null);
/**
* Create the map instance
* While `useEffect` could also be used here, `useLayoutEffect` will render
* the map sooner
*/
React.useLayoutEffect(() => {
// `mapRef.current` will be `undefined` when this hook first runs; edge case that
if (!mapRef.current) return;
const H = window.H;
const platform = new H.service.Platform({
apikey: "xxxxxxxxx"
});
const defaultLayers = platform.createDefaultLayers();
const hMap = new H.Map(mapRef.current, defaultLayers.vector.normal.map, {
center: { lat: 36.2104063, lng: -113.7236071 }, //,
zoom: 4,
pixelRatio: window.devicePixelRatio || 1
});
const behavior = new H.mapevents.Behavior(new H.mapevents.MapEvents(hMap));
const ui = H.ui.UI.createDefault(hMap, defaultLayers);
// This will act as a cleanup to run once this hook runs again.
// This includes when the component un-mounts
return () => {
hMap.dispose();
};
}, [mapRef]); // This will run this hook every time this ref is updated
return <div className="map" ref={mapRef} style={{ height: "355px",width:"650px" }} />;
};
const searchMap=(e)=>{
e.preventDefault();
console.log(" view controle ",e);
const origin="52.5308,13.3847";
const destiny="52.5264,13.3686";
}
I am trying to send coordinates from a start point to an end point so that I can show the plotted route and time
this worked for me:
Change API_KEY, origin and destination. Luck!
https://codesandbox.io/s/react-with-heremap-2cze0?file=/src/mapHere.js

Show Info bubble when clicking on a Marker in HERE Maps + REACT

So I'm currently developing a map-based application using React and HERE maps. I'm using the boilerplate in the documentation using Hooks. I'm firstly fetching the events and then displaying them in the useEffectLayout() hook, where the map loads.
import Paper from '#material-ui/core/Paper';
import useWindow from '../../CustomHooks/GetWindowSize/getSize';
import SearchBar from '../SearchBar/Search';
import {connect} from 'react-redux';
import {getEvents} from '../../actions/UserActions';
const HEREmap = ({userLocation, posts}) => {
const mapRef = React.useRef(null);
const size = useWindow();
//this is the method to add the marker to the map
const addEventsToMap = (events, H, hMap, ui) =>{
let markers = [];
events.map((el)=>{
var icon = new H.map.DomIcon(svgMarkup),
coords = {lat: el.Latitude, lng: el.Longitude},
marker = new H.map.DomMarker(coords, {icon: icon});
hMap.addObject(marker);
})
}
React.useLayoutEffect(() => {
if (!mapRef.current) return;
const H = window.H;
const platform = new H.service.Platform({
apikey: `${process.env.REACT_APP_API_KEY}`,
app_id: "XXXXXX"
});
const defaultLayers = platform.createDefaultLayers();
const hMap = new H.Map(mapRef.current, defaultLayers.vector.normal.map, {
center: { lat:userLocation.lat, lng: userLocation.lgn},
zoom: 13,
pixelRatio: window.devicePixelRatio || 1
});
//this add the user's location as a marker on the map
var icon = new H.map.DomIcon(svgMarkup),
coords = {lat: userLocation.lat, lng: userLocation.lgn},
marker = new H.map.DomMarker(coords, {icon: icon});
hMap.addObject(marker);
const behavior = new H.mapevents.Behavior(new H.mapevents.MapEvents(hMap));
const ui = H.ui.UI.createDefault(hMap, defaultLayers);
//this method gets called with the fetched events
addEventsToMap(posts, H, hMap, ui);
return () => {
hMap.dispose();
};
}, [mapRef]);
return (
<div className="map" ref={mapRef} style={{ height: size.height/1.5}}>
</div>
)
}
const mapStateToProps = state =>({
userA:state.userA
})
export default connect(mapStateToProps, {getEvents})(HEREmap);
I've read the documentation and it says to attach an onClick listener to the icon when it's created, but how do I add the info bubble to the same icon, when I need the reference to the ui as well as the information for each event?
Is there a more effective way to do it?
I'm not a React user, but I'd make the ui object available to your entire function, or the same scope as your map object. Does that help at all?

Resources