Using OpenLayers in React - reactjs

I am student who is creating a big project trying to display data and information on maps using Openlayers and React. At this point I have created a basic two page project where the map is the central piece of the application. For every page I have one main Component that contains its Sidebar, the Viewer Component that contains the map itself and some additional Components specific for that page.
The viewer Class is a special Component that I wrote for every page. This Component handles all the user interactions with the page. It sends and receives information from the main Component as well. I have chosen to create a seperate Viewer Class for each page beceause of the diffrent ways each page works. They might load in different things or handle interactions completely different. If I had one Viewer Class that can be used by every page this would become to large and have to many if statements to check what code to run when handling with each separate page.
Here is an example of the Homepage Viewer. In the constructor I create the map and add some references for a popup window that shows up when the user clicks on one of the features on the map. This popup window shows some basic information about the feature and let's the user add it to his list of features.
In ComponentDidMount I first make the Map class add the
boundrieslayer and give an function to call when one of the feautres
of this layer has been clicked on. I also create the overlay window
for the popup to the map. This will be shown when a feature has been
clicked.
resetMapLayers is a function that has will be called on each Render.
Here I make the map check if what background Tilelayer to use and if
it should show the top layer or not.
featureSelected() is the function that handles a click event of a
feature of the top layer. This will create a popup with the basic
information of the feature.
closePopup will be called when the popup has been closed. This will
make the map deselct the clicked on feature. Otherwise it would stay
selected when the popup has been closed. It will also remove the
Overlay(popup) from the screen.
addFeature() is the function that will be called when a user chooses
to add this feature to his list of features. This can be done by the
user in the popup window of that feature
.
import React, { Component } from "react";
import { connect } from "react-redux";
import Map from "../Map/Map";
import "ol/ol.css";
import styles from "./Viewer.module.scss";
import Overlay from "ol/Overlay";
import Button from "../UI/Button/Button";
class MainViewer extends Component {
constructor(props) {
super(props);
Map.createNewMap();
this.popup = React.createRef();
this.popupContent = React.createRef();
}
componentDidMount() {
Map.addBoundriesLayer(this.featureSelected);
Map.map.setTarget("map");
let container = this.popup.current;
let overlay = new Overlay({
element: container,
autoPan: true,
autoPanAnimation: {
duration: 250,
},
});
this.overlay = overlay;
Map.map.addOverlay(overlay);
}
resetMapLayers() {
Map.setBackgroundTileLayer(this.props.type);
Map.togglePlotBoundriesLayers(this.props.plotBoundriesState);
}
featureSelected = (event, select) => {
if (event.selected[0]) {
this.selectedFeature = event.selected[0];
let selectedFeature = {
id: event.selected[0].id_,
gewasgroepnaam: event.selected[0].getProperties().GEWASGROEP,
gewasnaam: event.selected[0].getProperties().LBLHFDTLT,
oppervlak: (event.selected[0].getProperties().OPPERVL / 10000).toFixed(
2
),
coords: event.selected[0].getProperties().geometry.extent_,
};
let content = this.popupContent.current;
content.innerHTML =
"<p><strong>Name: </strong>" +
selectedFeature.name +
"</p>" +
"<p><strong>Exact Name: </strong>" +
selectedFeature.exactName +
"</p>" +
"<p><strong>Area: </strong>" +
selectedFeature.area +
" ha</p>";
this.overlay.setPosition(event.mapBrowserEvent.coordinate);
}
};
closePopup() {
this.overlay.setPosition(undefined);
Map.clearSelect();
return false;
}
addFeature() {
this.overlay.setPosition(undefined);
Map.clearSelect();
this.props.featureAddedHandler(this.selectedFeature);
}
render() {
this.resetMapLayers();
return (
<div>
<div id="map" className={styles.Map}></div>
<div ref={this.popup} className={styles.OlPopup}>
<div className={styles.OlPopupButtonsDiv}>
<Button
btnType="Danger"
className={[styles.PopupButton, styles.ClosePopupButton].join(
" "
)}
clicked={() => this.closePopup()}
>
Annuleer
</Button>
<Button
btnType="Success"
className={[styles.PopupButton, styles.AddPopupButton].join(" ")}
clicked={() => this.addFeature()}
>
Voeg Toe
</Button>
</div>
<div ref={this.popupContent}></div>
</div>
</div>
);
}
}
const mapStateToProps = (state) => {
return {
type: state.mapDetails.type,
plotBoundriesState: state.mapDetails.state,
};
};
export default connect(mapStateToProps)(MainViewer);
The Map class contains the actual map. It handles with all the specific map issues. It allows to add layers to the map, creating the initial map, adding map interactions,...
In the constructor I call the methods to initaly create a basic map
with a TileLayer background.
The createMap and createNewMap methods let's me create the map object
itself.
createBackgroundLayerGroups() creates the Tilelayer that is the
background layer. It can be either OSM or Bing Maps. The visibility
property let's me make handle which one to show.
clearAllBoundriesLayers() deletes all the boundries layers that are
able to be put on top of the Tilelayer. It deletes every layer and
clears the select of the interactions that has been added to these
layers. I do this so when I change page the layers will be deleted.
addBoundriesLayer let's me set and add a boundries layer. This is the
Vector layer that will be put on top of the TileLayer.
addUsersPlotBoundriesLayer does the same as "addBoundriesLayer" but does it for all the layers that the user has. This function will be called in another page that only shows the features of the user.
setExtentOfMapByUserFeaters let's me set the extent of the map by
either a given extent or by the features of the user.
setInteractionForPlotBoundriesLayer adds the interaction for the
PlotBoundriesLayer.
setHoverInteractionForUserPlotBoundries adds the hover interaction
for the plotUserBoundriesLayer.
clearSelect clears all the selected features. So they won't be
highlighted no more.
setBackgroundTileLayer let's me show a specific background TileLayer.
togglePlotBoundriesLayers let's me hide or show the Vector layer that
is then been shown.
import Map from "ol/Map";
import TileWMS from "ol/source/TileWMS";
import TileLayer from "ol/layer/Tile";
import View from "ol/View";
import OSM from "ol/source/OSM";
import BingMaps from "ol/source/BingMaps";
import VectorSource from "ol/source/Vector";
import { bbox as bboxStrategy } from "ol/loadingstrategy";
import GeoJSON from "ol/format/GeoJSON";
import { Vector, Group, Tile } from "ol/layer";
import Select from "ol/interaction/Select";
import { Feature } from "ol";
import { Polygon } from "ol/geom";
import { Fill, Stroke, Style } from "ol/style";
class OlMap {
constructor() {
this.createNewMap();
this.createBackgroundLayerGroups();
}
createNewMap() {
this.map = this.createMap();
}
createMap() {
return new Map({
target: null,
layers: [],
view: new View({
center: [594668.0262129545, 6602083.305674396],
maxZoom: 19,
zoom: 14,
}),
});
}
createBackgroundLayerGroups() {
this.layersOSM = new Group({
layers: [
new Tile({
source: new OSM(),
}),
new Tile({
source: new BingMaps({
imagerySet: "Aerial",
key: process.env.REACT_APP_BING_MAPS,
}),
visible: false,
}),
],
});
}
clearAllBoundriesLayers() {
this.map.getLayers().forEach((layer) => {
if (
layer.get("name") === "plotBoundriesLayer" ||
layer.get("name") === "plotUserBoundriesLayer"
) {
layer.getSource().clear();
this.map.removeLayer(layer);
}
});
if (this.select) {
this.select.getFeatures().clear();
}
}
addBoundriesLayer(featureSelected) {
this.clearAllBoundriesLayers();
let vectorSource = new VectorSource({
format: new GeoJSON(),
minScale: 15000000,
loader: function (extent, resolution, projection) {
/*
Link for the DLV
let url = process.env.REACT_APP_MAP_API +
extent.join(",") +
",EPSG:3857";
*/ let url = process.env.REACT_APP_MAP_API +
extent.join(",") +
",EPSG:3857";
// */
let xhr = new XMLHttpRequest();
xhr.open("GET", url);
let onError = function () {
vectorSource.removeLoadedExtent(extent);
};
xhr.onerror = onError;
xhr.onload = function () {
if (xhr.status === 200) {
let features = vectorSource
.getFormat()
.readFeatures(xhr.responseText);
features.forEach(function (feature) {
//ID for the DLV
//feature.setId(feature.get("OBJ_ID"));
feature.setId(feature.get("OIDN"));
});
vectorSource.addFeatures(features);
} else {
onError();
}
};
xhr.send();
},
strategy: bboxStrategy,
});
let vector = new Vector({
//minZoom: 13,
source: vectorSource,
});
this.setInteractionForPlotBoundriesLayer(vector, featureSelected);
vector.set("name", "plotBoundriesLayer");
this.map.addLayer(vector);
}
addUsersPlotBoundriesLayer(featureSelected, featureHovered, newFeatures) {
this.clearAllBoundriesLayers();
if (newFeatures.length > 0) {
let vectorSource = new VectorSource({
format: new GeoJSON(),
minScale: 15000000,
strategy: bboxStrategy,
});
newFeatures.forEach((newFeature) => {
let feature = new Feature({
geometry: new Polygon([newFeature.geometry]),
});
feature.setId(newFeature.plotId);
vectorSource.addFeature(feature);
});
let vector = new Vector({
//minZoom: 13,
source: vectorSource,
});
this.setInteractionForPlotBoundriesLayer(vector, featureSelected);
this.setHoverInteractionForUserPlotBoundries(vector, featureHovered);
vector.set("name", "plotUserBoundriesLayer");
this.plotsExtent = vectorSource.getExtent();
this.map.addLayer(vector);
}
}
setExtentOfMapByUserFeaters(extent) {
if (extent === undefined) {
if (this.plotsExtent !== undefined && this.plotsExtent[0] !== Infinity) {
this.map.getView().fit(this.plotsExtent);
}
} else {
this.map.getView().fit(extent);
}
}
setInteractionForPlotBoundriesLayer(layer, featureSelected) {
this.select = new Select({
layers: [layer],
});
this.select.on("select", (event) => featureSelected(event, this.select));
this.map.addInteraction(this.select);
}
setHoverInteractionForUserPlotBoundries(layer, featureHovered) {
this.hoveredFeature = null;
let defaultStyle = new Style({
stroke: new Stroke({
width: 2,
color: "#9c1616",
}),
fill: new Fill({ color: "#c04e4e" }),
});
let hoveredStyle = new Style({
stroke: new Stroke({
width: 2,
color: "#9c1616",
}),
fill: new Fill({ color: "#9c1616" }),
});
this.map.on("pointermove", (e) => {
layer
.getSource()
.getFeatures()
.forEach((feature) => {
feature.setStyle(defaultStyle);
});
let newFeature = null;
this.map.forEachFeatureAtPixel(e.pixel, (f) => {
newFeature = f;
newFeature.setStyle(hoveredStyle);
return true;
});
if (newFeature) {
if (
this.hoveredFeature === null ||
this.hoveredFeature !== newFeature
) {
this.hoveredFeature = newFeature;
featureHovered(this.hoveredFeature.id_);
}
} else {
if (this.hoveredFeature !== null) {
this.hoveredFeature = null;
featureHovered(null);
}
}
});
}
hoveredSideBarFeatureHandler(hoveredFeatureId) {
let defaultStyle = new Style({
stroke: new Stroke({
width: 2,
color: "#9c1616",
}),
fill: new Fill({ color: "#c04e4e" }),
});
let hoveredStyle = new Style({
stroke: new Stroke({
width: 2,
color: "#9c1616",
}),
fill: new Fill({ color: "#9c1616" }),
});
this.map.getLayers().forEach((layer) => {
if (layer.get("name") === "plotUserBoundriesLayer") {
layer
.getSource()
.getFeatures()
.forEach((feature) => {
if (feature.id_ === hoveredFeatureId) {
feature.setStyle(hoveredStyle);
} else {
feature.setStyle(defaultStyle);
}
});
}
});
}
clearSelect() {
this.select.getFeatures().clear();
}
setBackgroundTileLayer(type) {
if (this.backgroundTileType === null) {
this.backgroundTileType = "OPENSTREETMAP";
}
if (this.map.getLayers().getArray().length === 0) {
this.map.setLayerGroup(this.layersOSM);
} else {
if (this.backgroundTileType !== type) {
this.backgroundTileType = type;
console.log(this.map.getLayers());
this.map.getLayers().getArray()[0].setVisible(false);
this.map.getLayers().getArray()[1].setVisible(false);
if (type === "OPENSTREETMAP") {
this.map.getLayers().getArray()[0].setVisible(true);
} else if (type === "BING MAPS") {
this.map.getLayers().getArray()[1].setVisible(true);
}
}
}
}
togglePlotBoundriesLayers(state) {
if (this.plotBoundriesState === null) {
this.plotBoundriesState = true;
}
if (this.plotBoundriesState !== state) {
this.plotBoundriesState = state;
this.map.getLayers().forEach((layer) => {
if (layer.get("name") === "plotBoundriesLayer") {
layer.setVisible(state);
}
if (layer.get("name") === "plotUserBoundriesLayer") {
console.log(state);
layer.setVisible(state);
}
});
}
}
addTileLayer(url) {
const wmsLayer = new TileLayer({
source: new TileWMS({
url,
params: {
TILED: true,
},
crossOrigin: "Anonymous",
}),
});
this.map.addLayer(wmsLayer);
}
}
export default new OlMap();
At this point I am wondering if I am doing things well or what can be done different or better to optimize my code. This will help me in the long run not getting stuck with bade code I created at the beginning of the project.
Many thanks in advance!

Related

React - PayPal Button fires without checking conditions

I'm using react-paypal-express-checkout
I've to options: Cash and PayPal.
Cash working fine and checks all conditions.
But bcs PayPal is a seperate component in my CartScreen component it opens and don't check a single if conditions and opens the PayPal window
The CashButton comes with function "cashTranSuccess" it's the same function as "TranSuccess"
just without the paymentID bcs it's only needed for react-paypal-express-checkout
So what I'm looking for is, to check all TranSuccess() conditions before open the PayPal window.
PayPalButton.js
import React from 'react';
import PaypalExpressBtn from 'react-paypal-express-checkout';
export default class PayPalButton extends React.Component {
render() {
const onSuccess = (payment) => {
// Congratulation, it came here means everything's fine!
console.log('The payment was succeeded!', payment);
// You can bind the "payment" object's value to your state or props or whatever here, please see below for sample returned data
this.props.tranSuccess(payment);
};
const onCancel = (data) => {
// User pressed "cancel" or close Paypal's popup!
console.log('The payment was cancelled!', data);
// You can bind the "data" object's value to your state or props or whatever here, please see below for sample returned data
};
const onError = (err) => {
// The main Paypal's script cannot be loaded or somethings block the loading of that script!
console.log('Error!', err);
// Because the Paypal's main script is loaded asynchronously from "https://www.paypalobjects.com/api/checkout.js"
// => sometimes it may take about 0.5 second for everything to get set, or for the button to appear
};
let env = 'sandbox'; // you can set here to 'production' for production
let currency = 'EUR'; // or you can set this value from your props or state
let carttotal = this.props.carttotal; // same a s above, this is the total amount (based on currency) to be paid by using Paypal express checkout
// Document on Paypal's currency code: https://developer.paypal.com/docs/classic/api/currency_codes/
const client = {
sandbox:
'',
production: 'YOUR-PRODUCTION-APP-ID',
};
// In order to get production's app-ID, you will have to send your app to Paypal for approval first
// For sandbox app-ID (after logging into your developer account, please locate the "REST API apps" section, click "Create App"):
// => https://developer.paypal.com/docs/classic/lifecycle/sb_credentials/
// For production app-ID:
// => https://developer.paypal.com/docs/classic/lifecycle/goingLive/
// NB. You can also have many Paypal express checkout buttons on page, just pass in the correct amount and they will work!
// Style Options: https://developer.paypal.com/docs/checkout/standard/customize/buttons-style-guide/ ; https://wise.com/gb/blog/custom-paypal-button
let style = {
size: 'medium',
color: 'gold',
label: 'pay',
tagline: false,
};
return (
<PaypalExpressBtn
env={env}
client={client}
currency={currency}
total={carttotal}
onError={onError}
shipping={1}
onSuccess={onSuccess}
onCancel={onCancel}
style={style}
/>
);
}
}
CartScreen
const tranSuccess = async (payment) => {
const { paymentID } = payment;
// Check time, min amoint, for delivery add delivery fees
if (timeValidation === true) {
if (sliderDeliveryValue === 'delivery') {
if (carttotal > settings[0]?.minDeliveryAmount) {
await axios.post(
'/api/payment',
{ cartItems, paymentID, time, sliderDeliveryValue, carttotal },
{
headers: { Authorization: token },
}
);
cartItems.map((remove) => {
dispatch(deleteFromCart(remove));
});
//console.log(cartItems.length);
toast.success(
'Order successful',
{
position: toast.POSITION.TOP_RIGHT,
}
);
} else {
toast.error(
`Min amount${settings[0]?.minDeliveryAmount}€`,
{
position: toast.POSITION.TOP_RIGHT,
}
);
}
} else if (sliderDeliveryValue === 'pickup') {
if (carttotal > 2) {
await axios.post(
'/api/payment',
{ cartItems, paymentID, time, sliderDeliveryValue, carttotal },
{
headers: { Authorization: token },
}
);
cartItems.map((remove) => {
dispatch(deleteFromCart(remove));
});
//console.log(cartItems.length);
toast.success(
'Order successful',
{
position: toast.POSITION.TOP_RIGHT,
}
);
} else {
toast.error(`Min amount 2.00€`, {
position: toast.POSITION.TOP_RIGHT,
});
}
} else {
toast.error('Choose delivery method', {
position: toast.POSITION.TOP_RIGHT,
});
}
} else {
toast.error('closed', {
position: toast.POSITION.TOP_RIGHT,
});
}
};
<PayPalButton
carttotal={carttotal}
tranSuccess={tranSuccess}
/>
<div onClick={cashTranSuccess}>
<CashButton />
</div>
Consider using the official #paypal/react-paypal-js
An example of validation using onInit and onClick functions and the actions.enable/disable callbacks or returning a promise (actions.resolve/reject) can be found in the developer documentation. Adapt this to check whatever condition you need.

QR shaded region not coming in Html5QrcodeScanner

I'm trying to build the barcode scanner using the html5-qrcode npm package in react js, everything working fine except QR shaded region. I added the qrbox in the configuration but not getting the QR shaded region. The qrbox width and height are not taken. I tried multiple ways not getting the desired result. Thanks in advance.
Here is my component:
import { Html5QrcodeScanner, Html5Qrcode } from "html5-qrcode";
import React from 'react';
const qrcodeRegionId = "html5qr-code-full-region";
class Html5QrcodePlugin extends React.Component {
render() {
return <div id={qrcodeRegionId} />;
}
componentWillUnmount() {
this.html5QrcodeScanner.clear().catch(error => {
console.error("Failed to clear html5QrcodeScanner. ", error);
});
}
componentDidMount() {
function createConfig(props) {
var config = {};
if (props.fps) {
config.fps = props.fps;
}
config.qrbox = { width: 250, height: 250 };
if (props.aspectRatio) {
config.aspectRatio = props.aspectRatio;
}
if (props.disableFlip !== undefined) {
config.disableFlip = props.disableFlip;
}
return config;
}
var config = createConfig(this.props);
var verbose = this.props.verbose === true;
if (!(this.props.qrCodeSuccessCallback )) {
throw "qrCodeSuccessCallback is required callback.";
}
this.html5QrcodeScanner = new Html5QrcodeScanner(
qrcodeRegionId, { facingMode: { exact: "environment"} }, config, verbose);
this.html5QrcodeScanner.render(
this.props.qrCodeSuccessCallback,
this.props.qrCodeErrorCallback);
}
};
export default Html5QrcodePlugin;

Display Info Window on the multiple coordinates on the arcgis map in Next JS

Here below is my next JS Code which is showing a simple ArcGIS map with the points or markers on the specific coordinates.
Can anyone please let me know that how can I display the popup / Info window for the points on the map? e.g. I click on any point and it will open a corresponding popup on it.
import NavBar from '#/components/NavBar'
import axios from 'axios';
import { useRef, useEffect, useState } from 'react';
import { loadModules } from 'esri-loader';
export default function Home({...props}) {
const [state, setState] = useState('');
const MapElement = useRef(null)
const options = {
url: 'https://js.arcgis.com/4.6/',
css: true
};
useEffect(() => {
var vehicleData = props.data
var map, point_symbol;
loadModules([
"esri/views/MapView",
"esri/WebMap",
"esri/Graphic",
"esri/geometry/Point",
"esri/PopupTemplate",
"esri/layers/FeatureLayer","dojo/domReady!"
],options).then(([ MapView, WebMap, Graphic, Point, PopupTemplate, FeatureLayer]) => {
const webmap = new WebMap({
basemap: "gray-vector"
})
var map = new MapView({
map: webmap,
center:[-6.357768833333333, 53.415487166666665],
zoom:6,
container: MapElement.current
})
map.popup.autoOpenEnabled = false;
for(var i=0, i_length=vehicleData.length; i<i_length; i++){
point_symbol = new Point({
longitude:vehicleData[i].longitude,
latitude: vehicleData[i].latitude,
spatialReference: { wkid: 3857 }
})
var template = new PopupTemplate({
title: vehicleData[i].company,
content: vehicleData[i].description
});
var graphic_symbol = new Graphic({
geometry: point_symbol,
symbol: {
type: "simple-marker",
style: "circle",
color: "orange",
size: "18px",
outline: {
color: [150, 200, 255],
width: 5
}
},
popupTemplate: template
});
map.graphics.add(graphic_symbol)
}
map.on("click", function(event) {
map.popup.open({
location: event.mapPoint,
features: [graphic_symbol]
});
});
// map.on("click", function(event) {
// console.log(vehicleData)
// map.popup.open({
// location: event.mapPoint, // location of the click on the view
// title: "You clicked here", // title displayed in the popup
// content: "Your description here" // content displayed in the popup
// });
// });
})
return () => {
if(!!map) {
map.destroy()
map=null
}
}
})
return (
<div id="home-container">
<NavBar />
<div className="app-wrapper" >
<div className="app-content">
<div className="no-padding">
<div className="row gy-4">
<div className="col-12">
<div style={{height:1000, width:1400}} ref={MapElement}></div>
</div>
</div>
</div>
</div>
</div>
</div>
)
}
export async function getServerSideProps(context) {
let response = await axios(process.env.BASE_URL +'/devices/all',{
headers : {
'Authorization' : 'Bearer ' + process.env.TOKEN
}
})
let data = await response.data
return {
props : {
data: data
}
}
}
I need to display pop-up or info window corresponding to each markup multiple circles on the map. In the above code, API call is done by getServerSideProps, and data as an array of objects is passed to the component using props.
I am able to display multiple circles on the map but don't know how to display info window corresponding to each marker?
I think that in your code you are having a context problem with the variable i. When the popup shows the value of i always gonna be vehicleData.length.
So you can solve the context problem using let instead of var, but I will suggest you another approach.
Declare template, the popup template, outside the loop, and use two new properties that we will add in the next step.
var template = new PopupTemplate({
title: "{company}",
content: "{description}"
outFields: ["*"],
fieldInfos: [
{ fieldName: "company" },
{ fieldName: "description" }
]
});
Add the information you want to display, to the attributes of the graphics, like this,
var graphic_symbol = new Graphic({
geometry: point_symbol,
symbol: {
type: "simple-marker",
style: "circle",
color: "orange",
size: "18px",
outline: {
color: [150, 200, 255],
width: 5
}
},
popupTemplate: template,
attributes: { // <- here
company: vehicleData[i].company,
description: vehicleData[i].description
}
});
Finally, let the click event search for the graphic, like this,
map.on("click", function(event) {
map.popup.open({
location: event.mapPoint,
fetchFeatures: true
});
});
BTW, I just keep the last step because you change default on purpose, I am not seeing the reason here but you must have one.
#cabesuon is right. you can remove the on click event and remove map.popup.autoOpenEnabled = false; too. after that clicking on the graphic will open the popup info by default.
for the table format you may want to use a function for content
// The following snippet shows how to use a function
// to create a simple node and display it in the popup template content
let template = new PopupTemplate({
title: "Population by Gender",
content: setContentInfo
});
function setContentInfo(feature){ // feature here is the graphic, you may access its properties for the table
// create a chart for example
let node = domConstruct.create("div", { innerHTML: "Text Element inside an HTML div element." });
return node;
}

How to get the Blob image preview in my Uppy Custom setup

I learn React and now I use the Uppy so user can select files for upload.
When user have select his file the files are hidden by settting showSelectedFiles={false}
I use my own Component to show the selected files and I get the files using this:
.on("file-added", (file) => {
const { setFile } = props;
setFile(file);
const newList = this.state.files.concat({ file });
this.setState({
files: { newList },
});
});
For each file added to the Dashboard the setFile(file); is sending the file object to my Custom view. The problem is that the preview image Blob that is auto created by the Dashboard is not present at this stage.
How can I get the files to my Custom GUI to show them including the image preview Blob?
I'm new to React and JavaScript so please be gentle:)
Complete code:
import React from "react";
import "#uppy/status-bar/dist/style.css";
import "#uppy/drag-drop/dist/style.css";
import "#uppy/progress-bar/dist/style.css";
import "./styles.css";
import "#uppy/core/dist/style.css";
import "#uppy/dashboard/dist/style.css";
const Uppy = require("#uppy/core");
// const Dashboard = require("#uppy/dashboard");
const GoogleDrive = require("#uppy/google-drive");
const Dropbox = require("#uppy/dropbox");
const Instagram = require("#uppy/instagram");
const Webcam = require("#uppy/webcam");
const Tus = require("#uppy/tus");
const ThumbnailGenerator = require("#uppy/thumbnail-generator");
const {
Dashboard,
DashboardModal,
DragDrop,
ProgressBar,
} = require("#uppy/react");
class DashboardUppy extends React.Component {
constructor(props) {
super(props);
this.form = React.createRef();
this.state = {
showInlineDashboard: false,
open: false,
files: [],
};
this.uppy = new Uppy({
id: "uppy1",
autoProceed: false,
debug: true,
allowMultipleUploads: true,
proudlyDisplayPoweredByUppy: true,
restrictions: {
// maxFileSize: 1000000,
maxNumberOfFiles: 100,
minNumberOfFiles: 1,
allowedFileTypes: null,
},
onBeforeFileAdded: (currentFile, files) => {
console.log(files);
const modifiedFile = Object.assign({}, currentFile, {
name: currentFile + Date.now(),
});
if (!currentFile.type) {
// log to console
this.uppy.log(`Skipping file because it has no type`);
// show error message to the user
this.uppy.info(`Skipping file because it has no type`, "error", 500);
return false;
}
return modifiedFile;
},
})
.use(Tus, { endpoint: "https://master.tus.io/files/" })
.use(GoogleDrive, { companionUrl: "https://companion.uppy.io" })
.use(Dropbox, {
companionUrl: "https://companion.uppy.io",
})
.use(Instagram, {
companionUrl: "https://companion.uppy.io",
})
.use(Webcam, {
onBeforeSnapshot: () => Promise.resolve(),
countdown: false,
modes: ["video-audio", "video-only", "audio-only", "picture"],
mirror: true,
facingMode: "user",
locale: {
strings: {
// Shown before a picture is taken when the `countdown` option is set.
smile: "Smile!",
// Used as the label for the button that takes a picture.
// This is not visibly rendered but is picked up by screen readers.
takePicture: "Take a picture",
// Used as the label for the button that starts a video recording.
// This is not visibly rendered but is picked up by screen readers.
startRecording: "Begin video recording",
// Used as the label for the button that stops a video recording.
// This is not visibly rendered but is picked up by screen readers.
stopRecording: "Stop video recording",
// Title on the “allow access” screen
allowAccessTitle: "Please allow access to your camera",
// Description on the “allow access” screen
allowAccessDescription:
"In order to take pictures or record video with your camera, please allow camera access for this site.",
},
},
}).use(ThumbnailGenerator, {
thumbnailWidth: 200,
// thumbnailHeight: 200 // optional, use either width or height,
waitForThumbnailsBeforeUpload: true
})
.on("thumbnail:generated", (file, preview) => {
const img = document.createElement("img");
img.src = preview;
img.width = 100;
document.body.appendChild(img);
})
.on("file-added", (file) => {
const { setFile } = props;
setFile(file);
const newList = this.state.files.concat({ file });
this.setState({
files: { newList },
});
});
}
componentWillUnmount() {
this.uppy.close();
}
render() {
const { files } = this.state;
this.uppy.on("complete", (result) => {
console.log(
"Upload complete! We’ve uploaded these files:",
result.successful
);
});
return (
<div>
<div>
<Dashboard
uppy={this.uppy}
plugins={["GoogleDrive", "Webcam", "Dropbox", "Instagram"]}
metaFields={[
{ id: "name", name: "Name", placeholder: "File name" },
]}
open={this.state.open}
target={document.body}
onRequestClose={() => this.setState({ open: false })}
showSelectedFiles={false}
/>
</div>
</div>
);
}
}
export default DashboardUppy;
Ran into this problem as well because I wanted to use the image preview to figure out the aspect ratio of the underlying image.
If you're using Dashboard or ThumbnailGenerator for Uppy, an event is emitted for every upload:
uppy.on('thumbnail:generated', (file, preview) => {
const img = new Image();
img.src = preview;
img.onload = () => {
const aspect_ratio = img.width / img.height;
// Remove image if the aspect ratio is too weird.
// TODO: notify user.
if (aspect_ratio > 1.8) {
uppy.removeFile(file.id);
}
}
});
I realize though that you already are looking for this event in your code. I guess to answer your question, just put your logic there instead of in file-added.

How to use react to implement google Place's Searches, to create markers on the map?

Im new with react and i have created a react project. I would like to know how i can use this default starter project to implement the code from:
code link from google. The code is as the following, credit to google, linked above.
Css file-
/* Always set the map height explicitly to define the size of the div
* element that contains the map. */
#map {
height: 100%;
}
/* Optional: Makes the sample page fill the window. */
html, body {
height: 100%;
margin: 0;
padding: 0;
}
HTML file-
<div id="map"></div>
<!-- Replace the value of the key parameter with your own API key. -->
<script src="https://maps.googleapis.com/maps/api/js?key=AIzaSyCkUOdZ5y7hMm0yrcCQoCvLwzdM6M8s5qk&libraries=places&callback=initMap" async defer></script>
Java script (pure js) file -
// This example requires the Places library. Include the libraries=places
// parameter when you first load the API. For example:
// <script src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&libraries=places">
var map;
var service;
var infowindow;
function initMap() {
var sydney = new google.maps.LatLng(-33.867, 151.195);
infowindow = new google.maps.InfoWindow();
map = new google.maps.Map(
document.getElementById('map'), {center: sydney, zoom: 15});
var request = {
query: 'Museum of Contemporary Art Australia',
fields: ['name', 'geometry'],
};
service = new google.maps.places.PlacesService(map);
service.findPlaceFromQuery(request, function(results, status) {
if (status === google.maps.places.PlacesServiceStatus.OK) {
for (var i = 0; i < results.length; i++) {
createMarker(results[i]);
}
map.setCenter(results[0].geometry.location);
}
});
}
function createMarker(place) {
var marker = new google.maps.Marker({
map: map,
position: place.geometry.location
});
google.maps.event.addListener(marker, 'click', function() {
infowindow.setContent(place.name);
infowindow.open(map, this);
});
}
I understand i need to create my key with google maps js and google places, however since Im new to react I'm unsure to how i could implement this into my new react project. Coudl some one show me how these files of code could be put together to be fit for a react project please. I apologies if i sound all over the place.
You can refer to this code I made. Remember to change the value of "YOUR_API_KEY" so that the map will work properly.
Here is the App.js code snippet:
import React from 'react';
import './App.css';
import Map from './components/placeSearch';
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
map: {}
}
}
handleMapLoad = (map) => {
this.setState({
map: map
})
}
render() {
return (
<div className="App">
<Map id="myMap" options={{center: { lat: 51.501904, lng: -0.115871 }, zoom: 13}} onMapLoad = {this.handleMapLoad}/>
</div>
);
}
}
export default App;
The code for Place Search can be found in the Map components in placeSearch.js. Change the value of your API key here.
import React from "react";
import ReactDOM from 'react-dom';
const map;
var markers = [];
var infowindow;
const API_KEY = "YOUR_API_KEY";
var place = [];
class Map extends React.Component {
constructor(props) {
super(props);
}
componentDidMount() {
const script = document.createElement('script');
script.type = 'text/javascript';
script.src = `https://maps.googleapis.com/maps/api/js?key=` + API_KEY + `&libraries=geometry,places`;
script.id = 'googleMaps';
script.async = true;
script.defer = true;
document.body.appendChild(script);
script.addEventListener('load', e => {
this.onScriptLoad()
})
}
onScriptLoad() {
map = new window.google.maps.Map(document.getElementById(this.props.id), this.props.options);
this.props.onMapLoad(map)
var request = {
query: 'Museum of Contemporary Art Australia',
fields: ['name', 'geometry'],
};
var service = new google.maps.places.PlacesService(map);
service.findPlaceFromQuery(request, function(results, status) {
if (status === google.maps.places.PlacesServiceStatus.OK) {
for (var i = 0; i < results.length; i++) {
var place = results[i];
var marker = new google.maps.Marker({
map: map,
position:place.geometry.location,
title: place.formatted_address,
});
markers.push(marker);
infowindow = new google.maps.InfoWindow();
marker.addListener('click', () => {
infowindow.setContent(place.name);
infowindow.open(map, marker);
});
}
map.setCenter(results[0].geometry.location);
}
})
}
render() {
return (
<div id = "root" >
<div className = "map" id = {this.props.id}/>
</div>
)
}
}
export default Map;
Hope this helps!

Resources