How can I get the H3 hexagons on a react-map-gl/deck.gl viewport? - deck.gl

I want to query for data based on the H3 hexagons that are visible on the viewport (and for new data on each viewport change). Is there anyway to achieve this with react-map-gl and deck.gl?

To get the hexagons inside the viewport, you need to get the bounding box of the current viewport. If you have the current viewport as {latitude, longitude, zoom, width, height} (which you probably have in your component state if you're using react-map-gl), you can get the viewport using viewport-mercator-project:
import WebMercatorViewport from 'viewport-mercator-project';
function bboxFromViewport(viewport) {
const {width, height} = viewport;
const projection = new WebMercatorViewport(viewport);
const [west, north] = projection.unproject([0, 0]);
const [east, south] = projection.unproject([width, height]);
return {north, south, east, west};
}
Then you can use the bounding box with h3.polyfill to get the list of contained hexagons at a given resolution:
const nw = [north, west];
const ne = [north, east];
const sw = [south, west];
const se = [south, east];
const hexes = h3.polyfill([nw, ne, se, sw], resolution);
Depending on your use case, you might want to expand the bounding box before calling polyfill, to get additional data outside the immediate viewport.
You also probably want to bound this on the viewport extent somehow, or you could end up with millions of hexagons on zoom out. One cheap hack I've used for this is to take a very rough estimate of the number of hexagons we'll get, and avoid calling polyfill if it's too high:
// Inexact, but it doesn't matter for our purposes
const KM_PER_DEGREE_LAT = 111.2;
function estimateHexagonsInBBox(bbox, width, height, res) {
// This is an extremely rough estimate, but we're just trying
// to get a reasonable order of magnitude
const aspect = width / height;
const latKm = (bbox.north - bbox.south) * KM_PER_DEGREE_LAT;
const lonKm = latKm * aspect;
return (latKm * lonKm) / h3.hexArea(res, h3.UNITS.km2);
}

Related

Vue Leaflet Setting custom TileLayer bounds

I'm trying to display a large number of images using leaflet and I'm having problems making the tileLayers display properly.
I'm extending the TileLayer class and overriding the createTile function like this:
`
let imageLayer = TileLayer.extend({
createTile: function (coords, done) {
const image = window.L.DomUtil.create("img");
image.src = `http://placekitten.com/g/200/300`;
image.crossOrigin = "";
image.onload = function () {
this.onload = function () {};
done(null, image);
};
return image;
}
});
Result
I want to change the position of the images and to only display them in areas I have already defined (for example between offsetting the image at 0,0 to be at 1,1 or using latLng
([8273.300000000001, 10618.02] , [9232.653535353536, 9658.666464646465])).
Ive tried a bunch of different methods and almost always came back to bounds, which don't work and setting the bounds doesn't change anything.
I don't want to use ImageOverlays because the images I'm trying to display are sliced and named with the leaflet naming scheme (0_0_1.png).

How to handle responsive design in expo

I am using react native expo and trying to make a app. But as the window size increase my each view get distorted i am using
marginTop:'33%'
but as i am expanding browser in right hand size then all my views starts going down. How can i manage responsive ness
I have also tried
import { Dimensions } from "react-native";
const { width, height } = Dimensions.get("window");
//Guideline sizes are based on standard ~5" screen mobile device
const guidelineBaseWidth = 390;
const guidelineBaseHeight = 844;
const screenSize = Math.sqrt(width * height) / 100;
const scale = size => (width / guidelineBaseWidth) * size;
const verticalScale = size => (height / guidelineBaseHeight) * size;
const moderateScale = (size, factor = 0.5) =>
size + (scale(size) - size) * factor;
export { scale, verticalScale, moderateScale, screenSize };
In this i am using moderateScale but not getting any good benefit from it. So please check and let me how can i make apps to show on web as well as mobile size.

Does pdf.js allow rendering of a selected (rectangular) part of a page instead of rendering an entire page to a canvas?

Does pdf.js allow to render a PDF page only partially? More specifically, is it possible to tell pdf.js to render a selected "rectangle of pixels" out of an entire PDF page?
Assuming a resolution of 144 dpi, a typical page (DIN A4) would have approx. 684 (width) by 1190 (height) pixels. I would like to render (for example) a rectangle like [100, 100] (top left coordinate in pixels) and [400, 400] (bottom right coordinate in pixels).
A typical use case could be a scanned document with several handwritten notes that I would like to display and further process individually.
I do understand that a "workaround" could be to save the entire page as jpg (or any other suitable bitmap format) and apply some clipping function. But this would for sure be a less performant approach than selected rendering.
pdfs.js uses a viewport object (presumably containing parameters) for rendering. This object contains
height
width
offsetX
offsetY
rotation
scale
transform
viewBox (by default [0, 0, width / scale, height / scale])
One might think that manipulating the viewBox inside it might lead to the desired outcome, but I have found that changing the viewBox parameters does not do anything at all. The entire page is rendered every time that I apply the render method.
What might I have done wrong? Does pdf.js offer the desired functionality? And if so, how can I get it to work? Thank you very much!
Here is a very simple React component demonstrating my approach (that does not work):
import React, { useRef } from 'react';
import { pdfjs } from 'react-pdf';
pdfjs.GlobalWorkerOptions.workerSrc = 'pdf.worker.js';
function PdfTest() {
// useRef hooks
const myCanvas: React.RefObject<HTMLCanvasElement> = useRef(null);
const test = () => {
const loadDocument = pdfjs.getDocument('...');
loadDocument.promise
.then((pdf) => {
return pdf.getPage(1);
})
.then((page) => {
const viewport = page.getViewport({ scale: 2 });
// Here I modify the viewport object on purpose
viewport.viewBox = [100, 100, 400, 400];
if (myCanvas.current) {
const context = myCanvas.current.getContext('2d');
if (context) {
page.render({ canvasContext: context, viewport: viewport });
myCanvas.current.height = viewport.height;
myCanvas.current.width = viewport.width;
}
}
});
};
// Render function
return (
<div>
<button onClick={test}>Test!</button>
<canvas ref={myCanvas} />
</div>
);
}
export default PdfTest;
My initial thought was also to modify a viewBox of page Viewport. This was not the right guess (I hope that you already figured it out).
What do you need really to do to project only a part of a page to canvas is to prepare correctly the transformation of Viewport.
So it will look more or less like following:
const scale = 2
const viewport = page.getViewport({
scale,
offsetX: -100 * scale,
offsetY: - 100 * scale
})
This will move your your box section to the beginning of the canvas coordinates.
What probably you would like to do next is to make a canvas equal to the selected rectangle size (in your case is 300x300 scaled by your scale) and this solved the issue in my case.

For What people need this line of Code (Device Height)?

I have seen this line of code:
import { Dimensions, Platform } from 'react-native';
const { height, width } = Dimensions.get('window');
const deviceUtils = (function () {
const iPhone6Height = 667,
iphoneSEHeight = 568,
iPhoneXHeight = 812,
iPhoneXWidth = 375;
return {
dimensions: {
height,
width,
},
iPhone6Height,
iphoneSEHeight,
iPhoneXHeight,
iPhoneXWidth,
isIOS14: ios && parseFloat(Platform.Version as string) >= 14,
isLargePhone: width >= iPhoneXWidth,
isNarrowPhone: width < iPhoneXWidth,
isSmallPhone: height <= iPhone6Height,
isTallPhone: height >= iPhoneXHeight,
isTinyPhone: height <= iphoneSEHeight,
};
})();
export default deviceUtils;
I want to ask you why People need this exact height of iPhones ?
It seems someone plans to write different code for “wide” and “narrow” and “wide”, and for three different classes of heights. They use the exact width / height of an iPhone X for example to make sure that an iPhone X falls into the wide and tall categories.
This is not usually what you should do. Decide what you want to be on a screen. Use constraints to fit everything. If you have problems fitting everything use a smaller font or an adjustable font, or put everything in a scroll view. Anyway, with notch and things at the bottom of the screen, things are more complicated than just “width” and “height”.

Layout centered on one node

Is there a way to require that a layout doesn't change the position of one "root" node while considering it during the algorithm ? Or equivalently, is there a way to always center the camera to this node/keep the camera at the same relative position to this node ?
A bit of context. I am working on an iteratively built graph. Each time parts are added to the graph, the layout is completed. The graph may grow too big to be printed on a screen, and alternatives to the fit options are welcome. What is important though, is that the user is able to follow the node he selected. The best would be that this node doesn't move.
Here is an outline of the general approach. It can be applied with whatever strategy you like re. zoom and pan. The main thing is that you need to know the start and end positions of each node. So you run the layout in a batch, creating a visual nop, and then run a preset layout with the desired zoom and pan.
const clone = obj => Object.assign({}, obj);
const savePos1 = n => n.scratch('_layoutPos1', clone(n.position()));
const savePos2 = n => n.scratch('_layoutPos2', clone(n.position()));
const restorePos1 = n => n.position(n.scratch('_layoutPos1'));
const getPos2 = n => n.scratch('_layoutPos2');
cy.startBatch();
const nodes = cy.nodes();
const layout = cy.layout(myLayoutOptions); // n.b. animate:false
const layoutstop = layout.promiseOn('layoutstop');
nodes.forEach(savePos1);
layout.run();
await layoutstop;
nodes.forEach(savePos2);
nodes.forEach(restorePos1);
cy.endBatch();
cy.layout({
name: 'preset',
animate: true,
positions: getPos2,
// specify zoom and pan as desired
zoom,
pan
}).run();