I want to make an interactive app where users can view crime rates in their area by pulling in geo-data from the met police website.
I want to display hexagons across the map (either through using H3 Uber library, or an alternative) but essentially it needs to uniformly cover the world map. I have gotten the Mapbox map to display but I am trying to figure out how exactly would I render the hexagons on to the map. Like where would I put it.
Below is my code. Where can I improve it?
import React from "react";
import { StyleSheet, View } from "react-native";
import MapboxGL from "#rnmapbox/maps";
MapboxGL.setAccessToken("<ACCESS-TOKEN-HERE>");
const App = () => {
return (
<View style={styles.page}>
<View style={styles.container}>
<MapboxGL.MapView style={styles.map} />
</View>
</View>
);
};
export default App;
const styles = StyleSheet.create({
page: {
flex: 1,
justifyContent: "center",
alignItems: "center",
},
container: {
height: "100%",
width: "100%",
},
map: {
flex: 1,
},
});
Related
I'm in trouble while implementing the doughnut chart over OpenStreetMap. I'm using react-chartjs2 for the doughnut chart and react-leaflet for Openstreetmap. Like we use the location icon on different coordinates over the map but here I want to use a Doughnut graph over the map instead of the location icon.
I want to achieve something like this
As per the react-leaflet documentation, the Marker icon property accepts two types of icons that is icon strings like image URL and divIcon which can be some HTML elements but while I'm rendering react component it does not accept and not showing it.
Here you can check in codesandbox I have added code to make it easy to try
https://codesandbox.io/s/doughnut-chart-over-osm-map-1indvl?file=/src/App.js
For what I know marker Icons can only be static, I use a function to create my only markers based on icons and plain html. Will be hard to do that with a component in your case.
My icon render function
import { divIcon } from "leaflet";
import { ReactElement } from "react";
import { renderToString } from "react-dom/server";
export const createLeafletIcon = (
icon: ReactElement,
size: number,
className?: string,
width: number = size,
height: number = size
) => {
return divIcon({
html: renderToString(icon),
iconSize: [width, height],
iconAnchor: [width / 2, height],
popupAnchor: [0, -height],
className: className ? className : "",
});
};
In your case I would try to cheese it and create blank markers and show the graph in popups instead and just force the popups to alway stay open.
EDIT: Added my custom Marker code below that have some nice options.
You can just use the defaultOpen option, and add the graph as a child component to the marker and it will show up in the popup. You can the change the styling of you liking to make it look like the graph is the marker.
import { LatLngLiteral } from "leaflet";
import React, { Children, ReactElement, useEffect, useRef } from "react";
import { Marker, Popup, useMap } from "react-leaflet";
import { MapPin } from "tabler-icons-react";
import { createLeafletIcon } from "./utils";
export interface LeafletMarkerProps {
position: LatLngLiteral;
flyToPosition?: boolean;
size?: number;
color?: string;
icon?: ReactElement;
defaultOpen?: boolean;
onOpen?: () => void;
children?: React.ReactNode;
markerType?: string;
zIndexOffset?: number;
}
const LeafletMarker: React.FC<LeafletMarkerProps> = ({
position,
flyToPosition = false,
children,
size = 30,
color,
defaultOpen = false,
onOpen,
icon = <MapPin size={size} color={color} />,
markerType,
zIndexOffset,
}) => {
const map = useMap();
const markerRef = useRef(null);
position && flyToPosition && map.flyTo(position);
const markerIcon = createLeafletIcon(icon, size, markerType); // Important to not get default styling
useEffect(() => {
if (defaultOpen) {
try {
// #ts-ignore
if (markerRef.current !== null && !markerRef.current.isPopupOpen()) {
// #ts-ignore
markerRef.current.openPopup();
}
} catch (error) {}
}
}, [defaultOpen, position.lat, position.lng]);
return (
<Marker
eventHandlers={{
popupopen: () => onOpen && onOpen(),
}}
ref={markerRef}
icon={markerIcon}
position={position}
zIndexOffset={zIndexOffset}
>
{/* autoPan important to not have jittering */}
<Popup autoPan={false}>{children}</Popup>
</Marker>
);
};
export default LeafletMarker;
I'm trying to test a custom react-leaflet component. It grabs the map element and uses a filter to get the number of elements visible at the current zoom level:
const visible = allPoints.filter(p => map.getBounds().contains(p));
This code works fine in the application, but when I try to test with Jest & Enzyme, the filter always returns 0 elements, regardless of zoom level. Poking at it a bit, I discovered that map.getSize() always returns 0x0 and map.getBounds() returns the same point for the southwest and northeast corners, which explains why it can't really contain any other points.
Is this just a bug in Enzyme or Leaflet or is there something that I need to do to set the map size explicitly?
import React from "react";
import { mount } from "enzyme";
import { act } from "react-dom/test-utils";
import { MapContext } from "../../../context";
import MarkersShown from "../markers-shown";
import { Map } from "react-leaflet";
import { mockParsed, mockCenter, mockData } from "./__data__";
describe("MarkersShown Tests", () => {
it("renders the total asset count", async () => {
const parsed = mockParsed;
const center = mockCenter;
let wrapper;
await act(async () => {
wrapper = mount(
<MapContext.Provider value={mockData}>
<Map zoom={parsed.z} center={center} style={{ height: "100px", width: "100px" }}>
<MarkersShown />
</Map>
</MapContext.Provider>
);
});
wrapper.update();
expect(wrapper.html()).toEqual(
expect.stringMatching("Markers Shown of 3")
);
});
});
I am using React PixiOverlay wrapper (https://github.com/knapcio/react-leaflet-pixi-overlay) which has the nice ability to draw markers at scale on a leaflet map.
While this provides me with a really nice way to draw hundreds of thousands of markers efficiently, I do not know how to programmatically select these markers. I want to be able to draw a shape on the map and select the markers within that geometry. I have the ability to draw the shape, but I don't know how to search and select for the markers inside.
Any help would be very much appreciated.
EDIT: For those wondering how the initial rendering is done, there are an array of markers that are passed, which PixiOverlay reads and the renders. The color, marker type, coordinates, etc are all passed in this array.
import PixiOverlay from 'react-leaflet-pixi-overlay';
import { Map } from 'react-leaflet';
import { renderToString } from 'react-dom/server';
const App = () => {
const markers = [{
id: 'randomStringOrNumber',
iconColor: 'red',
position: [-37.814, 144.96332],
popup: renderToString(
<div>All good!</div>
),
onClick: () => alert('marker clicked'),
tooltip: 'Hey!',
},
{
id: '2',
iconColor: 'blue',
position: [-37.814, 144.96332],
popup: 'Quack!',
popupOpen: true, // if popup has to be open by default
onClick: () => alert('marker clicked'),
tooltip: 'Nice!',
}
];
return {
<Map
preferCanvas={true}
maxZoom={20}
minZoom={3}
center={[-37.814, 144.96332]}
// Other map props...
>
<TileLayer
url="https://{s}.basemaps.cartocdn.com/rastertiles/voyager/{z}/{x}/{y}{r}.png"
attribution='© OpenStreetMap contributors'
/>
<PixiOverlay markers={markers} />
</Map>
};
}
Is there any way how to enhance a backdrop from example in https://material-ui.com/components/backdrop/ to show loading circle only above the single component (in case some page has more component), not above the whole page?
Thanks for reply.
Backdrop are fixed positioned by default, that's why it covers the whole page.
To achieve the result you want, we have to change its position to absolute and contain it inside an element with relative position — this element can be your component. If you're new in CSS positions check this docs from developer.mozilla.org.
Knowing all that, we can come up with the following codes
const useStyles = makeStyles({
parent: {
position: "relative",
width: 200,
height: 200,
backgroundColor: "red",
zIndex: 0,
},
backdrop: {
position: "absolute"
}
});
export default function App() {
const classes = useStyles();
return (
<div className={classes.parent}>
<Backdrop className={classes.backdrop} open={true}>
<CircularProgress color="inherit" />
</Backdrop>
</div>
);
}
Also we have to define z-index on either parent or backdrop element to make it work. Not sure why though.
I created a codesandbox for you to play with.
The Backdrop component of Material UI is set to position: 'fixed' by default, that's why it covers the whole page.
If you want it to reside and position itself like any other component typically on the DOM, all you have to do is to reset its position back to relative, for instance:
<Backdrop open={true} sx={{ position: 'relative' }}>
<CircularProgress color="inherit" />
</Backdrop>
and you don't need to change the parent component since it should be in your case see to relative by default if you're not changing it. But if you have crazy positions going in your app here and there, then you might consider changing that as well.
i have created a custom componenet that i use if i want to block only part of the UI:
"use strict";
/** external libraries */
import React from "react";
import Backdrop from "#mui/material/Backdrop";
import CircularProgress from "#mui/material/CircularProgress";
const BlockUi = ({open, onClose, children}) => {
return (
<div style={{"position": "relative"}}>
<Backdrop
sx={{color: "#FFFFFF", zIndex: (theme) => theme.zIndex.drawer + 1, "position": "absolute"}}
open={open}
onClick={() => onClose()}
>
<CircularProgress color="inherit"/>
</Backdrop>
{children}
</div>
);
}
export default BlockUi;
and i use it like this:
"use strict";
/** external libraries */
import React from "react";
import BlockUi from "./BlockUi";
const JsonForm = ({fields, onSubmit}) => {
const [loading, setLoading] = React.useState(false)
const stopLoading = () => {
setLoading(false)
}
return (
<div>
<BlockUi open={loading} onClose={stopLoading}>
<button type="submit" onClick={() => {
console.log(loading)
setLoading(true)
}}>Submit
</button>
</BlockUi>
</div>
);
}
export default JsonForm;
I need to add a square frame in the middle of the viewfinder of the react native camera view. There is not information about it in the repository also. And questions there is not being answered also.
Which module you are using? react-native-camera or react-native-camera-kit?
If you using react-native-camera just put View (or Image) inside Camera component, then add styles to vertically and horizontally align this view.
Like this:
const styles = {
container: {
flex: 1,
},
camera: {
flex: 1,
// These below are most important, they center your border view in container
// ref: https://css-tricks.com/snippets/css/a-guide-to-flexbox/
alignItems: "center",
justifyContent: "center"
},
borderImage: {
// Your styles for image, or custom borders
},
}
class Component extends React.Component {
...
render(){
return <View style={styles.container}>
<Camera style={styles.camera}>
<Image style={styles.borderImage} source={require("./img/qrBorder.png")} />
</Camera>
</View>;
}
}