Add tiles from sentinel-hub to mapbox-gl - reactjs

I tried to add tiles from sentinel-hub to mapbox-gl.
This is the first time I'm doing this and I didn't succeed.
import mapboxgl from 'mapbox-gl';
import "./Map.css"
mapboxgl.accessToken = 'your token';
export default class MapComponent extends React.Component {
constructor(props) {
super(props)
this.state = {
lng: 7.56198,
lat: 47.47607,
zoom: 9,
}
}
componentDidMount() {
const { lat, lng, zoom } = this.state;
map = new mapboxgl.Map({
container: this.mapDiv,
style: 'mapbox://styles/mapbox/streets-v11',
center: [lng, lat],
zoom: zoom
});
map.on('load', () => {
map.addSource('portland', {
'type': 'raster',
'url': 'https://services.sentinel-hub.com/ogc/wmts/{INSTANCE_ID}?REQUEST=GetTile&bbox=bbox-epsg-3857&RESOLUTION=10&LAYER=TRUE-COLOR-S2L2A&TILEMATRIXSET=PopularWebMercator256&TILEMATRIX=10&TILEROW=307&TILECOL=775'
});
map.addLayer({
'id': 'portland',
'source': 'portland',
'type': 'raster'
});
});
};
render() {
return (
<div>
<div ref={e => this.mapDiv = e} className="map"></div>
<nav id="menu"></nav>
</div>
)
}
}
Maybe I need to do something with 'url' address or 'addSourse/addLayer'. Tell me, what is the error and how to fix it?

Related

Change this react class based component to a function one with hooks

I'm trying to add a similar feature (an interactive map with popups) to a react application I'm working on. I'm trying to change this into a function component with hooks but have been having some problems (specifically with handleResults and addMarker). I'm relatively new to coding, sorry if this seems really trivial.
import React, { Component } from 'react';
import ReactMapGl, {Marker, Popup} from 'react-map-gl'
import Geocoder from 'react-map-gl-geocoder'
import 'mapbox-gl/dist/mapbox-gl.css';
import 'react-map-gl-geocoder/dist/mapbox-gl-geocoder.css'
const mapboxToken = 'Your API Key'
class Map3 extends Component {
constructor() {
super()
this.state = {
viewport: {
width: '100vw',
height: '100vh',
latitude: 49.28,
longitude: -123.12,
zoom: 14
},
currMarker: null,
markers: [],
selectedMarker: null
}
this.handleViewportChange = this.handleViewportChange.bind(this)
this.handleGeocoderViewportChange = this.handleGeocoderViewportChange.bind(this)
this.handleResult = this.handleResult.bind(this)
this.addMarker = this.addMarker.bind(this)
this.handleClose = this.handleClose.bind(this)
this.handleMarkerClick = this.handleMarkerClick.bind(this)
}
mapRef = React.createRef()
handleViewportChange(viewport) {
this.setState(prevState => ({
viewport: {...prevState.viewport, ...viewport}
}))
}
handleGeocoderViewportChange(viewport) {
const geocoderDefaultOverrides = {transitionDuration: 1000}
return this.handleViewportChange({
...viewport,
...geocoderDefaultOverrides
})
}
handleResult (result) {
this.setState({
currMarker: {
name: result.result.place_name,
latitude: result.result.center[1],
longitude: result.result.center[0]
}
})
}
addMarker() {
const {currMarker} = this.state
this.setState(prevState => ({
markers: [...prevState.markers, currMarker],
currMarker: null
}))
}
handleMarkerClick(marker) {
this.setState({
selectedMarker: marker
})
}
handleClose = () => {
this.setState({
selectedMarker: null
})
}
render() {
const {viewport, markers, selectedMarker} = this.state
return (
<ReactMapGl
ref={this.mapRef}
{...viewport}
onViewportChange={viewport => this.setState({viewport})}
mapboxApiAccessToken={mapboxToken}
mapStyle="mapbox://styles/mapbox/streets-v10"
>
<button className="add-btn" onClick={this.addMarker}>Add</button>
{markers.map((marker, idx) => {
return (
<Marker
key={idx}
latitude={marker.latitude}
longitude={marker.longitude}
onClick={() => this.handleMarkerClick(marker)}
>
<img src="pin.png" alt="marker"/>
</Marker>
)
})
}
<Geocoder
onSelected={this.handleResult}
mapRef={this.mapRef}
placeholder="Search here!"
onViewportChange={this.handleGeocoderViewportChange}
onResult={this.handleResult}
mapboxApiAccessToken={mapboxToken}
position="top-right"
/>
{this.state.selectedMarker &&
<Popup
mapRef={this.mapRef}
latitude={selectedMarker.latitude}
longitude={selectedMarker.longitude}
closeButton={true}
closeOnClick={false}
onClose={this.handleClose}
>
<h3>{selectedMarker.name}</h3>
</Popup>
}
</ReactMapGl>
)
}
}
export default Map3;

TypeError: Cannot read property 'getCanvasContainer' of undefined in React mapbox-gl project

I'm building a React Mapbox project with mapboxgl (note: not the React wrapper version). So far I've been able to add what I'd like to the map and to my database, but I'm having trouble displaying all the data as markers/points/popups.
The message I get in Chrome is TypeError: Cannot read property 'getCanvasContainer' of undefined.
I'm wondering if this is a problem with my map itself (and lack of React wrapper for Mapbox) or with the coordinates. Right now I'm manually setting the coordinates as const coords, but if I don't, I get the message: Error: LngLatLike argument must be specified as a LngLat instance, an object {lng: , lat: }, an object {lon: , lat: }, or an array of [, ] which I've also been unable to solve. Here's some of my code to check out.
export default class Map extends Component {
constructor(props) {
super(props);
this.state = {
allBuildings: [],
lng: 5,
lat: 45,
zoom: 3,
}
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(4)
});
});
let marker = new mapboxgl.Marker({
draggable: true,
color: "blue"
}).setLngLat([16.40, 50.54]).addTo(map)
marker.on('dragend', () => {
const lngLat = marker.getLngLat();
this.setState({
lng: lngLat.lng,
lat: lngLat.lat
})
})
this.getMarkers()
}
getMarkers = () => {
axios
.get('/api/add')
.then(res => {
this.setState({
allBuildings: res.data
res.data.forEach(building => {
const popup = new mapboxgl.Popup({ offset: 10})
const coords = [building.lng, building.lat]
new mapboxgl.Marker({
color: 'orange',
draggable: false })
.setPopup(popup)
.setLngLat(building.lng, building.lat)
.setHTML(<div>add later</div>)
.addTo(this.map)
})
})
.catch(err => {
console.log(err)
})
}

React MapboxGL rendering issue with function component

I'm very new at programming and react. Currently trying to bring a layer to MapboxGL Map but I become error msg it says:
'mapContainer' is not defined no-undef
What am I doing wrong?
import React, {useEffect} from 'react';
import mapboxgl from 'mapbox-gl';
mapboxgl.accessToken = 'PUBLIC TOKEN'
const getMap = () => {
return new mapboxgl.Map({
container: mapContainer,
style: 'mapbox://styles/mapbox/streets-v11',
center: [9, 47],
zoom: 10
});
const Map = () => {
useEffect(() => {
const map = getMap();
map.on('move', () => {
this.setState({
lng: map.getCenter().lng.toFixed(4),
lat: map.getCenter().lat.toFixed(4),
zoom: map.getZoom().toFixed(0)
});
});
map.on('load', () => {
map.addLayer({
id: 'streets',
type: 'line',
source: {
type: 'geojson',
data:
'http://someWFSAPIdata=application/json'
},
layout: {
'line-join': 'round',
'line-cap': 'round'
},
paint: {
'line-color': '#08363e',
'line-width': 0.8
}
});
});
}, []);
And rendering as followed
return (
<div>
<div ref={el => (this.mapContainer = el)} className='mapContainer' />
<h1>Hello there geoReact</h1>
</div>
);
Since I'm using functional component no longer need to render but I'm not sure what is wrong with it.
Thanks a lot
I found this explanation which was very helpful for my case
https://sparkgeo.com/blog/build-a-react-mapboxgl-component-with-hooks/

How to setState() from within a nested function in React?

I'm trying to adapt this example from https://github.com/mapbox/mapbox-react-examples/tree/master/basic,
import React from 'react'
import ReactDOM from 'react-dom'
import mapboxgl from 'mapbox-gl'
mapboxgl.accessToken = 'pk.eyJ1IjoibWFwYm94IiwiYSI6ImNpejY4M29iazA2Z2gycXA4N2pmbDZmangifQ.-g_vE53SD2WrJ6tFX7QHmA';
class Application extends React.Component {
constructor(props: Props) {
super(props);
this.state = {
lng: 5,
lat: 34,
zoom: 1.5
};
}
componentDidMount() {
const { lng, lat, zoom } = this.state;
const map = new mapboxgl.Map({
container: this.mapContainer,
style: 'mapbox://styles/mapbox/streets-v9',
center: [lng, lat],
zoom
});
map.on('move', () => {
const { lng, lat } = map.getCenter();
this.setState({
lng: lng.toFixed(4),
lat: lat.toFixed(4),
zoom: map.getZoom().toFixed(2)
});
});
}
render() {
const { lng, lat, zoom } = this.state;
return (
<div>
<div className="inline-block absolute top left mt12 ml12 bg-darken75 color-white z1 py6 px12 round-full txt-s txt-bold">
<div>{`Longitude: ${lng} Latitude: ${lat} Zoom: ${zoom}`}</div>
</div>
<div ref={el => this.mapContainer = el} className="absolute top right left bottom" />
</div>
);
}
}
ReactDOM.render(<Application />, document.getElementById('app'));
to a case in which, rather than displaying the map's center, I would like to display the latitude and longitude of the mouse position.
So far, I've managed to simply log it to the console:
import React from 'react';
import mapboxgl from 'mapbox-gl';
mapboxgl.accessToken = 'pk.eyJ1Ijoia3VydHBlZWsiLCJhIjoiY2p6cnVneWdvMHlzeDNqcWo0dm83ZzZ2eiJ9.yUCSreTRcKs12uT5PTCztg';
export default class Map extends React.Component {
componentDidMount() {
this.map = new mapboxgl.Map({
container: this.mapContainer,
style: 'mapbox://styles/mapbox/outdoors-v11',
center: [-119.5591, 37.715],
zoom: 9
});
this.map.on('load', function(e) {
e.target.on('mousemove', function(e) {
console.log(JSON.stringify(e.point));
console.log(JSON.stringify(e.lngLat.wrap()));
});
});
}
componentWillUnmount() {
this.map.remove();
}
render() {
const style = {
position: 'absolute',
top: 0,
bottom: 0,
width: '100%'
};
return <div style={style} ref={el => this.mapContainer = el} />;
}
}
This writes lines like the following to the console:
{"x":972,"y":272}
{"lng":-118.90266689452113,"lat":37.86205552587528}
However, rather than logging the coordinates to the console, I would like to invoke this.setState() like in the example so that I can render the coordinates in a child component.
The problem is, within the on('mousemove', ...) callback function, this is not the component. I've read about using arrow functions (which are lexically scoped) to work around this, but it seems to me that in this case, I need a 'normal' function(e) in order to capture the event.
How can I setState() with the mouse coordinates in this example?
It's possible to use arrow functions just like any other function
this.map.on('load', e => {
e.target.on('mousemove', e => {
this.setState({}) //correct this
})
})

How to add coordinates to google-maps-react on a user click event?

I'm trying to add a marker on based on a user clicking on the map for the google-maps-react npm. Currently the code below will generate markers and add them to the this.state = { markers:[ ] } and I would like to map them out on the map component. However, the position:event.latLng, will not register the lat and lng and the marker will only be created and inserted into the state with the key: Date.now() and defaultAnimation: 2. Below the code:
import React, { Component } from 'react';
import {Map, InfoWindow, Marker, GoogleApiWrapper} from 'google-maps-react';
export class MapContainer2 extends Component {
constructor(props){
super(props);
this.state={
lat:null,
lng:null,
markers:[]
}
}
componentDidMount(){
navigator.geolocation.getCurrentPosition(position=>
this.setState({
lat:position.coords.latitude,
lng:position.coords.longitude,
}));
}
mapClicked = (event) =>{
const { markers } = this.state;
this.setState({
markers:[
{
position:event.latLng,
key: Date.now(),
defaultAnimation: 2,
},
...markers
]
})
}
render() {
if (!this.props.loaded) {
return <div>Loading...</div>
}
const style = {
width: '100%',
height: '100vh'
}
return (
<Map
google={this.props.google}
zoom={11}
style={style}
initialCenter={{
lat: this.state.lat,
lng: this.state.lng
}}
center={{
lat: this.state.lat,
lng: this.state.lng
}}
onClick={this.mapClicked}
>
<Marker
title={'Geolocation'}
position={{
lat:this.state.lat,
lng:this.state.lng,
}}
/>
</Map>
);
}
}
export default GoogleApiWrapper({
apiKey: ('AIzaSyCZ7rgMN34kWkGvr8Pzkf_8nkT7W6gowBA')
})(MapContainer2)
I resolved it by updating the function mapClicked with the following:
mapClicked = (mapProps, map, event) => {
const { markers } = this.state;
const lat = event.latLng.lat();
const lng = event.latLng.lng();
let url = `https://maps.googleapis.com/maps/api/geocode/json?latlng=${lat},${lng}&key=myapikey`
axios.get(url).then(response => {
this.setState({
googleReverseGeolocation:response.data.results[0].formatted_address,
markers:[{position:{lat:event.latLng.lat(),lng:event.latLng.lng()}}, ...markers],
latClick:lat,
lngClick:lng
});
this.props.onMapClickChange(lat, lng, response.data.results[0].formatted_address);
});
}

Resources