REACT: add geojson data to deck.gl & react-map-gl - reactjs

I am trying to graph a very simple data here https://raw.githubusercontent.com/pennytalalak/road-crash-data/master/src/data/output.geojson
But it wouldn't display the data on the map. Not sure if it is my geojson file or my code as my console said
Here is my code:
import React, { Component } from "react";
import { StaticMap } from "react-map-gl";
import DeckGL, { GeoJsonLayer } from "deck.gl";
//Mapbox Token
const MAPBOX_TOKEN =
"###########################";
//Geojson Data URL
const DATA =
"https://raw.githubusercontent.com/pennytalalak/road-crash-data/master/src/data/output.geojson";
const LIGHT_SETTINGS = {
lightsPosition: [-125, 50.5, 5000, -122.8, 48.5, 8000],
ambientRatio: 0.2,
diffuseRatio: 0.5,
specularRatio: 0.3,
lightsStrength: [2.0, 0.0, 1.0, 0.0],
numberOfLights: 2
};
const INITIAL_VIEW_STATE = {
latitude: -35.280937,
longitude: 149.130005,
zoom: 13,
pitch: 0,
bearing: 0
};
export default class Map extends Component {
renderLayer() {
const { data = DATA } = this.props;
return [
new GeoJsonLayer({
id: "geoJson",
data,
filled: true,
lightSettings: LIGHT_SETTINGS
})
];
}
render() {
const { viewState, baseMap = true } = this.props;
return (
<DeckGL
layers={this.renderLayer()}
initialViewState={INITIAL_VIEW_STATE}
viewState={viewState}
controller={true}
>
{baseMap && (
<StaticMap
mapStyle="mapbox://styles/mapbox/dark-v9"
preventStyleDiffing={true}
mapboxApiAccessToken={MAPBOX_TOKEN}
/>
)}
</DeckGL>
);
}
}

Related

Convert from class-based component to functional-based component

I am trying to use the react-native-maps-directions example found in https://www.npmjs.com/package/react-native-maps-directions on my project.
Can someone help me convert this code from class base component to functional base component.
import React, { Component } from 'react';
import { Dimensions, StyleSheet } from 'react-native';
import MapView from 'react-native-maps';
import MapViewDirections from 'react-native-maps-directions';
const { width, height } = Dimensions.get('window');
const ASPECT_RATIO = width / height;
const LATITUDE = 37.771707;
const LONGITUDE = -122.4053769;
const LATITUDE_DELTA = 0.0922;
const LONGITUDE_DELTA = LATITUDE_DELTA * ASPECT_RATIO;
const GOOGLE_MAPS_APIKEY = '…';
class Example extends Component {
constructor(props) {
super(props);
// AirBnB's Office, and Apple Park
this.state = {
coordinates: [
{
latitude: 37.3317876,
longitude: -122.0054812,
},
{
latitude: 37.771707,
longitude: -122.4053769,
},
],
};
this.mapView = null;
}
onMapPress = (e) => {
this.setState({
coordinates: [
...this.state.coordinates,
e.nativeEvent.coordinate,
],
});
}
render() {
return (
<MapView
initialRegion={{
latitude: LATITUDE,
longitude: LONGITUDE,
latitudeDelta: LATITUDE_DELTA,
longitudeDelta: LONGITUDE_DELTA,
}}
style={StyleSheet.absoluteFill}
ref={c => this.mapView = c}
onPress={this.onMapPress}
>
{this.state.coordinates.map((coordinate, index) =>
<MapView.Marker key={`coordinate_${index}`} coordinate={coordinate} />
)}
{(this.state.coordinates.length >= 2) && (
<MapViewDirections
origin={this.state.coordinates[0]}
waypoints={ (this.state.coordinates.length > 2) ? this.state.coordinates.slice(1, -1): undefined}
destination={this.state.coordinates[this.state.coordinates.length-1]}
apikey={GOOGLE_MAPS_APIKEY}
strokeWidth={3}
strokeColor="hotpink"
optimizeWaypoints={true}
onStart={(params) => {
console.log(`Started routing between "${params.origin}" and "${params.destination}"`);
}}
onReady={result => {
console.log(`Distance: ${result.distance} km`)
console.log(`Duration: ${result.duration} min.`)
this.mapView.fitToCoordinates(result.coordinates, {
edgePadding: {
right: (width / 20),
bottom: (height / 20),
left: (width / 20),
top: (height / 20),
}
});
}}
onError={(errorMessage) => {
// console.log('GOT AN ERROR');
}}
/>
)}
</MapView>
);
}
}
export default Example;
Can someone help me convert this code from class base component to functional base component.
Thanks.
i'm not react native use (i only use the reactjs) but this is my try it may help (i can't test if it work or not).
import React from 'react'
import { useState ,useRef} from 'react';
import { Dimensions, StyleSheet } from 'react-native';
import MapView from 'react-native-maps';
import MapViewDirections from 'react-native-maps-directions';
const { width, height } = Dimensions.get('window');
const ASPECT_RATIO = width / height;
const LATITUDE = 37.771707;
const LONGITUDE = -122.4053769;
const LATITUDE_DELTA = 0.0922;
const LONGITUDE_DELTA = LATITUDE_DELTA * ASPECT_RATIO;
const GOOGLE_MAPS_APIKEY = '…';
const Example = () => {
const [coordinates, setCoordinates] = useState([{
latitude: 37.3317876,
longitude: -122.0054812,
},
{
latitude: 37.771707,
longitude: -122.4053769,
},
],)
let mapView = useRef(null)
let onMapPress = (e) => {
setCoordinates((coordinates) =>
[
...coordinates,
e.nativeEvent.coordinate,
],
);
}
return (
<MapView
initialRegion={{
latitude: LATITUDE,
longitude: LONGITUDE,
latitudeDelta: LATITUDE_DELTA,
longitudeDelta: LONGITUDE_DELTA,
}}
style={StyleSheet.absoluteFill}
ref={c => mapView.current = c}
onPress={onMapPress}
>
{coordinates.map((coordinate, index) =>
<MapView.Marker key={`coordinate_${index}`} coordinate={coordinate} />
)}
{(coordinates.length >= 2) && (
<MapViewDirections
origin={coordinates[0]}
waypoints={(coordinates.length > 2) ? coordinates.slice(1, -1) : undefined}
destination={coordinates[coordinates.length - 1]}
apikey={GOOGLE_MAPS_APIKEY}
strokeWidth={3}
strokeColor="hotpink"
optimizeWaypoints={true}
onStart={(params) => {
console.log(`Started routing between "${params.origin}" and "${params.destination}"`);
}}
onReady={result => {
console.log(`Distance: ${result.distance} km`)
console.log(`Duration: ${result.duration} min.`)
mapView.current.fitToCoordinates(result.coordinates, {
edgePadding: {
right: (width / 20),
bottom: (height / 20),
left: (width / 20),
top: (height / 20),
}
});
}}
onError={(errorMessage) => {
// console.log('GOT AN ERROR');
}}
/>
)}
</MapView>
)
}
export default Example;

Changing color on layer React-Mapbox-gl

I am new to React-mapbox GL. I have tried for a while now to look at the examples but can't figure out how to change the layer's color on enter/hover. I have 2 questions so far.
map.on('mouseenter', 'clusters', () => {
map.getCanvas().style.cursor = 'pointer';
});
How can I define the the cluster element for each function in Reactmapbox gl? I don't quite understand how the interactiveLayerIds works I suppose?
question 2.
const onMouseEnter = useCallback(event =>{
if (event.features[0].layer.id==="unclustered-point"){
/* console.log(event.features[0].layer.paint.'circle-color') */
}
})
I have attempted this so far(the whole code is below) but it tells me that circle-color is a unexpected token. OnEnter this unclustered-point layer I want to change the color of the element so the user can clearly see what element they are hovering over? How would I go about doing this in React mapbox gl if I cant change the circle color?
THE WHOLE CODE:
import React, { useContext, useEffect, useRef,useState,useCallback } from 'react';
import './MapViews.css';
import { useNavigate } from 'react-router-dom';
import ReactMapGL, { Marker, Layer, Source } from 'react-map-gl';
import SourceFinder from '../../Apis/SourceFinder';
import { SourceContext } from '../../context/SourceContext';
import { clusterLayer, clusterCountLayer, unclusteredPointLayer } from './Layers';
const MapView = () => {
const navigate = useNavigate()
const { sources, setSources } = useContext(SourceContext)
const [viewport, setViewport] = React.useState({
longitude: 10.757933,
latitude: 59.91149,
zoom: 12,
bearing: 0,
pitch: 0
});
const mapRef = useRef(null);
function getCursor({isHovering, isDragging}) {
return isDragging ? 'grabbing' : isHovering ? 'pointer' : 'default';
}
useEffect(() => {
const fetchData = async () => {
try {
const response = await SourceFinder.get("/sources");
setSources(response.data.data.plass);
} catch (error) { }
};
fetchData();
}, [])
const onMouseEnter = useCallback(event =>{
if (event.features[0].layer.id==="unclustered-point"){
/* console.log(event.features[0].layer.paint.'circle-color') */
}
})
const ShowMore = event => {
if(event.features[0].layer.id==="unclustered-point"){
const feature = event.features[0];
console.log(feature)
mapRef.current.getMap().getCanvas().style.cursor="pointer"
}else{
const feature = event.features[0];
const clusterId = feature.properties.cluster_id;
const mapboxSource = mapRef.current.getMap().getSource('stasjoner');
mapboxSource.getClusterExpansionZoom(clusterId, (err, zoom) => {
if (err) {
return;
}
setViewport({
...viewport,
longitude: feature.geometry.coordinates[0],
latitude: feature.geometry.coordinates[1],
zoom,
transitionDuration: 500
});
});
}
};
return (
<ReactMapGL {...viewport} width="100%" height="100%" getCursor={getCursor} onMouseEnter={onMouseEnter} interactiveLayerIds={[clusterLayer.id,unclusteredPointLayer.id]} mapboxApiAccessToken={"SECRET"} clickRadius={2} onViewportChange={setViewport} mapStyle="mapbox://styles/mapbox/streets-v11" onClick={ShowMore} ref={mapRef}>
<Source id="stasjoner" type="geojson" data={sources} cluster={true} clusterMaxZoom={14} clusterRadius={50} >
<Layer {...clusterLayer} />
<Layer {...clusterCountLayer} />
<Layer {...unclusteredPointLayer}/>
</Source>
</ReactMapGL>
);
};
export default MapView;
LAYERS.JS
//Hvergang vi skal ha 2 eller flere baller
export const clusterLayer = {
id: 'clusters',
type: 'circle',
source: 'stasjoner',
filter: ['has', 'point_count'],
paint: {
'circle-color': ['step', ['get', 'point_count'], '#51bbd6', 100, '#f1f075', 500, '#f28cb1'],
'circle-radius': ['step', ['get', 'point_count'], 20, 100, 30, 750, 40],
}
};
//Dette er tallene som er inne i ballene
export const clusterCountLayer = {
id: 'cluster-count',
type: 'symbol',
source: 'stasjoner',
filter: ['has', 'point_count'],
layout: {
'text-field': '{point_count_abbreviated}',
'text-font': ['DIN Offc Pro Medium', 'Arial Unicode MS Bold'],
'text-size': 12,
}
};
//Per punkt
export const unclusteredPointLayer = {
id: 'unclustered-point',
type: 'circle',
source: 'stasjoner',
filter: ['!', ['has', 'point_count']],
paint: {
'circle-color': '#11b4da',
'circle-radius': 8,
'circle-stroke-width': 1,
'circle-stroke-color': '#fff',
}
};

How to display geodjango rest api response on react leaflet Map

I have the following response from django rest:
[
{
"area": 0.0,
"perimeter": 0.0,
"town_name": "Cheptais",
"town_id": 4,
"town_type": "Market Centres",
"geom": "SRID=4326;MULTIPOINT (34.4500007629395 0.800000011920929)"
},
{
"area": 0.0,
"perimeter": 0.0,
"town_name": "Dadaab",
"town_id": 3,
"town_type": "Trading Centre",
"geom": "SRID=4326;MULTIPOINT (40.3199996948242 0.070000000298023)"
},
{
"area": 0.0,
"perimeter": 0.0,
"town_name": "Eldas",
"town_id": 4,
"town_type": "Market Centres",
"geom": "SRID=4326;MULTIPOINT (39.5499992370605 2.52999997138977)"
}
]
Fetching the endpoint with axios this way:
await axios
.get("/api/gis/areas/", headers)
.then((response) => {
this.setState({ places: response.data });
console.log(response.data);
})
.catch(function (error) {
console.log(error);
});
}
const handleEachFeature = (feature, layer) => {
layer.bindPopup('<font size="4">' + feature.properties.town_name);
}
Using react leaflet, I create a map instance as follows:
<Map className="map" onEachFeature={handleEachFeature} style={{height:'100%',width:'100%'}}>
<GeoJSON data={places}/>
</Map>
However, this does not overlay the api response on my map.. I'm I missing something?
as i mentioned on the comments,you have to convert the wkt to geojson in order for this to work, there are several solution to achieve such conversion but the easiest one here is to import wicket library (just use npm install wicket), also you need to create the with a unique key, here is a working component from your same data (note i'm not using axios as i test the data locally), :
import React, { Component } from 'react'
import './styles/styles.css'
import {Map,TileLayer,GeoJSON} from 'react-leaflet'
import './leaflet/leaflet.css'
import Wkt from 'wicket'
import L from 'leaflet'
import Data from '../../Data/wkt_file.json'
import icon from 'leaflet/dist/images/marker-icon.png';
import iconShadow from 'leaflet/dist/images/marker-shadow.png';
// this is for maker to show up:
let DefaultIcon = L.icon({
iconUrl: icon,
shadowUrl: iconShadow
});
L.Marker.prototype.options.icon = DefaultIcon;
export default class map extends Component {
constructor(props){
super(props);
this.state={
wkt_json_holder:[],
json_ob:<></>,
json_key:1,
tile:'https://tiles.stadiamaps.com/tiles/alidade_smooth_dark/{z}/{x}/{y}{r}.png',
}
this.setGeoJSON = this.setGeoJSON.bind(this)
this.onEach = this.onEach.bind(this)
}
async componentDidMount(){
await this.setState({wkt_json_holder:Data});
this.setGeoJSON()
}
setGeoJSON=()=>{
let json_data = this.state.wkt_json_holder.map(point=>{
let wkt_geom = point['geom'].replace('SRID=4326;','')
let wkt = new Wkt.Wkt();
wkt.read(wkt_geom)
let geojson_geom = wkt.toJson()
let coords = geojson_geom['coordinates']
let type = geojson_geom['type']
let geojson_obj={
"type": "Feature",
"geometry": {
'type':type,
'coordinates':coords
},
"properties": {
"town_name": point['town_name'], "town_id": point['town_id'], "town_type":point['town_type'], "perimeter": point['perimeter'], "area": point['area']
}
}
return geojson_obj
}
)
console.log(json_data)
let json_ob= <GeoJSON data={json_data} key={1} style={{color:'red'}} onEachFeature={this.onEach}/>
this.setState({json_ob})
}
// handling Popups
onEach(feature,layer){
console.log(feature)
let PopupContent = `
<Popup>
<p>town id:${feature.properties.town_id}</p>
<p>town name:${feature.properties.town_name}</p>
</Popup>
`
layer.bindPopup(PopupContent)
}
render() {
return (
<div style={{width:'100%',height:'100%'}}>
<Map center={[2.197035, 38.703588]} zoom={6} style={{width:'100%',height:'100%'}}>
<TileLayer url={this.state.tile}/>
{this.state.json_ob}
</Map>
</div>
)
}
}

Custom marker not rerendering after coordinates change

I'm trying to move some markers around the map but after update the array which contains the info about the coordinates of them nothing happens, it's just blinking but not moving.
BusMarker.js (Customer Marker)
import React, { useState, useEffect } from 'react'
import { Marker } from 'react-native-maps'
const BusMarker = props => {
const { longitude, latitude, plate } = props
const [coordinates] = useState({
longitude: Number(longitude),
latitude: Number(latitude)
})
return (
<Marker
key={`bus-${plate}-${new Date().getMilliseconds()}`}
coordinate={coordinates}
image={require('../../img/icon/bus-icon.png')}>
</Marker>
)
}
export default BusMarker
Map.js
import React, { useState, useEffect, Fragment, useCallback, useRef } from 'react'
import { StyleSheet, View, Text } from 'react-native'
import { useSelector, useDispatch } from 'react-redux'
import MapView, { PROVIDER_GOOGLE} from 'react-native-maps'
import BusMarker from './BusMarker'
import { useFocusEffect } from 'react-navigation-hooks'
const Maps = () => {
const [busLocation, setBusLocation] = useState([
{
"plate": "CAR1203",
"latitude": 0,
"longitude": 0,
},
])
const [region] = useState({
latitude: 0,
longitude: 0,
latitudeDelta: 0.0143,
longitudeDelta: 0.0134,
})
const _renderBusesOnMap = busLocation => {
if (busLocation.length > 0) {
return busLocation.map(({ plate, longitude, latitude }) => {
return (
<BusMarker key={plate} plate={plate} longitude={longitude} latitude={latitude} />
)
})
}
}
const updateLocations = () => {
const newPosition = [
{
"plate": "CAR1203",
"latitude": 0,
"longitude": 1,
},
]
const timeout = setInterval(() => {
setBusLocation(newPosition)
}, 1000)
}
useEffect(updateLocations, [])
return (
<MapView
provider={PROVIDER_GOOGLE}
style={styles.map}
initialRegion={region}
showsCompass={false}
showsTraffic={false}
showsIndoors={false}
showsBuildings={false}
showsPointsOfInterest={false}
loadingEnabled={false}
>
{_renderBusesOnMap(busLocation)}
</MapView>
)
}
const styles = StyleSheet.create({
map: {
flex: 1,
backgroundColor: '#ffff',
height: '100%'
},
})
export default Maps
Why is it not updating its location on the map?
I'm using the following versions on this project:
React Native: 0.60.5
React Native Maps: 0.25.0
Try this instead:
const BusMarker = props => {
const { longitude, latitude, plate } = props;
const coordinates = {
longitude: Number(longitude),
latitude: Number(latitude)
};
return (
<Marker
key={`bus-${plate}-${new Date().getMilliseconds()}`}
coordinate={coordinates}
/>
);
};
const Maps = () => {
const [busLocation, setBusLocation] = useState([
{
plate: "CAR1203",
latitude: 37.78825,
longitude: -122.4324
}
]);
const [region] = useState({
latitude: 37.78825,
longitude: -122.4324,
latitudeDelta: 0.0922,
longitudeDelta: 0.0421
});
const _renderBusesOnMap = busLocation => {
if (busLocation.length > 0) {
return busLocation.map(({ plate, longitude, latitude }) => {
return (
<BusMarker
key={plate}
plate={plate}
longitude={longitude}
latitude={latitude}
/>
);
});
}
};
const updateLocations = () => {
const newPosition = [
{
plate: "CAR1203",
latitude: 37.78845,
longitude: -122.4424
}
];
const timeout = setInterval(() => {
setBusLocation(newPosition);
}, 1000);
};
useEffect(updateLocations, []);
return (
<MapView
provider={PROVIDER_GOOGLE}
style={styles.map}
initialRegion={region}
showsCompass={false}
showsTraffic={false}
showsIndoors={false}
showsBuildings={false}
showsPointsOfInterest={false}
loadingEnabled={false}
>
{_renderBusesOnMap(busLocation)}
</MapView>
);
};
The main problem I think is the way you set coordinates inside BusMarker. There is no need to use useState there. You can just set coordinates as an object with the latitude and longitude values you receive from props.
I've adjusted the coordinates to make it easier to test.
Update
I will try to add to this answer, by explaining my understanding of the behavior. I will explain by giving a more general example. So let's say you have code like this:
const Component = props => {
const [data, setData] = useState(props);
// ...
Now props will be set as the initial value of data.
But useState won't update on props change this way. So even though the props have changed, data still holds the value it was initially passed. To tell data to update on props change you could use useEffect inside Component:
useEffect(() => {
setData(props);
}, [props]);
But like I said in my original answer, there is no reason to use useState in this situation. We don't need to keep Component's state in sync with the parent props as we can simply use props directly. So don't use the useEffect approach I've given as it's unnecessary, it's just to give an example.

I am using reactC3js module to load a chart an I am not able to load y axis dynamically

I am using reactC3js module for creating line charts and not avle to load y axis data dynamically , the y axis takes the data sent first and after that it is not updating and re-rendering the chart
Here an example I did for you.
https://codesandbox.io/s/wkyw9xrj7k
Demo : https://wkyw9xrj7k.codesandbox.io/
Hope this will help you.
import React from "react";
import ReactDOM from "react-dom";
import C3Chart from "react-c3js";
import "c3/c3.css";
import "./styles.css";
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
chartData: {
columns: [
["data1", 30, 200, 100, 400, 150, 250],
["data2", 50, 20, 10, 40, 15, 25]
]
}
};
}
//Dynamically change the X-axis value with random numbers..
changeX = () => {
let chartData = this.state.chartData;
let newX = chartData.columns[0].map((n, i) => {
return i > 0 ? (Math.random() * 100).toFixed(2) : n;
});
chartData.columns[0] = newX;
this.setState({ chartData });
};
//Dynamically change the Y-axis value with random numbers similar to changeX.
changeY = () => {
let chartData = this.state.chartData;
let newY = chartData.columns[1].map((n, i) => {
return i > 0 ? (Math.random() * 100).toFixed(2) : n;
});
chartData.columns[1] = newY;
this.setState({ chartData });
};
render() {
return (
<div>
<C3Chart data={this.state.chartData} />
<button onClick={() => this.changeX()}>changeX</button>
<button onClick={() => this.changeY()}>changeY</button>
</div>
);
}
}
const mountNode = document.getElementById("root");
ReactDOM.render(<App />, mountNode);

Resources