Further work on polygons. Multipolygons still fail.

master
Sander Vocke 6 years ago
parent 042401d61e
commit 2dd2f2111c
  1. 1
      package.json
  2. 7
      src/database.js
  3. 34
      src/map.js
  4. 10
      src/resultsview.js
  5. 70
      src/userquerywidget.js
  6. 5
      yarn.lock

@ -22,6 +22,7 @@
"leaflet": "^1.6.0",
"lodash": "^4.17.15",
"object-hash": "^2.0.1",
"point-in-polygon": "^1.0.1",
"prop-types": "^15.6.0",
"react": "^16.12.0",
"react-dom": "^16.12.0",

@ -49,13 +49,10 @@ export function regexp_match(string, regex) {
return string.match(regex) != null;
}
export function is_in_polygon(x, y, poly) {
return false;
}
export function is_in_geo_polygon_from_store(lat, long, polygon_hash) {
const poly = get_polygon_from_store(polygon_hash);
return is_in_polygon(lat, long, poly);
var inside = require('point-in-polygon');
return inside([lat, long], poly);
}
// Digikam stores its tree of tags as individual tags,

@ -10,6 +10,11 @@ import "leaflet/dist/leaflet.css"
import L from 'leaflet';
export function MapView(props) {
// This is bad React design: this component holds state which the parent needs (the Leaflet map object).
// Normally we'd make such an object in the parent and pass it as a prop. However, the object cannot
// be created without a div being present for it to reside in, so we have a chicken-egg problem.
// We solve it by loading the map after mounting this component, then passing a handle to the parent.
const { onMapChange } = props;
const [map, setMap] = useState(null);
const [camera, setCameraNoPush] = useState([51.505, -0.09, 13]); //lat, long, zoom
@ -35,6 +40,14 @@ export function MapView(props) {
}
));
}, [])
// Notify parent if map object changes.
useEffect(() => {
if (onMapChange) {
onMapChange(map);
}
}, map);
// Update camera state of this component when map changes.
useEffect(() => {
if (map != null) {
@ -47,24 +60,6 @@ export function MapView(props) {
}
}, [map])
function showNominatimResult(result) {
var corner1 = L.latLng(result.boundingbox[0], result.boundingbox[2]);
var corner2 = L.latLng(result.boundingbox[1], result.boundingbox[3]);
var bounds = L.latLngBounds(corner1, corner2);
map.flyToBounds(bounds);
}
function onSearch(query) {
fetch("http://nominatim.openstreetmap.org/search?polygon_geojson=1&polygon_threshold=0.001&format=json&limit=5&q=" + query)
.then(res => res.json())
.then(jsonres => {
//console.log("Nominatim result: ", jsonres);
if (Array.isArray(jsonres) && jsonres.length > 0) {
showNominatimResult(jsonres[0]);
}
});
}
const style = {
width: "100%",
height: "600px",
@ -72,9 +67,6 @@ export function MapView(props) {
return (
<>
<Box>
<SearchBar onSubmit={onSearch} />
</Box>
<Box>
<div id={id} style={style}></div>
</Box>

@ -1,4 +1,4 @@
import React from 'react';
import React, { useState, useEffect } from 'react';
import { makeStyles } from '@material-ui/core/styles';
import Box from '@material-ui/core/Box';
@ -10,7 +10,7 @@ import Tabs from '@material-ui/core/Tabs';
import Tab from '@material-ui/core/Tab';
import { GridGallery } from './gridgallery.js';
import { MapView } from './map.js';
import { create_map, MapView } from './map.js';
const useStyles = makeStyles(theme => ({
root: {
@ -107,9 +107,9 @@ function TabPanel(props) {
export function ResultsView(props) {
const classes = useStyles();
const { photos, albums, tags } = props;
const [ activeIndex, setActiveIndex ] = React.useState(0);
const [ activeIndex, setActiveIndex ] = useState(0);
var _ = require('lodash');
const _ = require('lodash');
function tabProps(index) {
return {
@ -120,7 +120,7 @@ export function ResultsView(props) {
const handleChange = (e, newindex) => {
setActiveIndex(newindex);
}
return (
<div className={classes.root}>
<AppBar position="static">

@ -1,4 +1,4 @@
import React, { useEffect, useContext } from 'react';
import React, { useEffect, useState } from 'react';
import Switch from '@material-ui/core/Switch';
import Box from '@material-ui/core/Box';
@ -21,9 +21,13 @@ import { MuiPickersUtilsProvider, DateTimePicker } from "@material-ui/pickers";
import DateFnsUtils from '@date-io/date-fns';
import { format } from 'date-fns';
import L from 'leaflet';
import { makeStyles } from '@material-ui/core/styles';
import { MapView, create_map } from './map.js';
import { SearchBar } from './searchbar.js';
import {
filter_is_const_false, ConstFilter, LogicalOperatorFilter, MatchingFilter,
ResultTypeEnum, LogicalOperatorEnum, MatchTypeEnum, NegationFilter, TimeFilterTypeEnum,
@ -53,13 +57,75 @@ const useStyles = makeStyles(theme => ({
margined: {
margin: "6px",
},
mapcontainer: {
width: "600px",
},
}));
export function EditLocationFilterExpression(props) {
const { onChange, filter } = props;
const [map, setMap] = useState(null);
const [proposal, setProposal] = useState(filter.polygon);
const [polyLayers, setPolyLayers] = useState([]);
const classes = useStyles();
const _ = require('lodash');
function updateProposal(result) {
// TODO: handle multi-polies
const polygon = result.geojson.coordinates[0].map(longlat => [ longlat[1], longlat[0] ]);
// Show the polyline on the map
polyLayers.forEach(layer => {
map.removeLayer(layer);
});
var polyline = L.polyline(polygon, { color: 'blue' }).addTo(map);
map.flyToBounds(polyline.getBounds());
setPolyLayers([polyline]);
// Update the proposed polygon
console.log("Updated proposal:", result);
setProposal(polygon);
}
function onSearch(query) {
if (map == null) {
return;
}
fetch("http://nominatim.openstreetmap.org/search?polygon_geojson=1&polygon_threshold=0.001&format=json&limit=5&q=" + query)
.then(res => res.json())
.then(jsonres => {
//console.log("Nominatim result: ", jsonres);
if (Array.isArray(jsonres) && jsonres.length > 0) {
updateProposal(jsonres[0]);
}
});
}
function handleUseProposal() {
var new_filter = _.cloneDeep(filter);
new_filter.polygon = proposal;
onChange(new_filter);
}
// This component is a bit tricky. MapView is not very React-y because it manages its
// own state inside (this is due to how Leaflet is integrated).
// We don't want to render a new MapView whenever our filter changes.
// Therefore we ensure that filter changes don't trigger a new MapView render.
return (
<Typography>TODO</Typography>
<>
<Box>
<SearchBar onSubmit={onSearch} />
</Box>
<Box className={classes.mapcontainer}>
<MapView onMapChange={setMap}></MapView>
</Box>
<Box>
<Button variant="contained" onClick={handleUseProposal}>Use shown boundary</Button>
<Typography>Polygon with {filter.polygon.length} points.</Typography>
</Box>
</>
);
}

@ -7782,6 +7782,11 @@ pnp-webpack-plugin@1.5.0:
dependencies:
ts-pnp "^1.1.2"
point-in-polygon@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/point-in-polygon/-/point-in-polygon-1.0.1.tgz#d59b64e8fee41c49458aac82b56718c5957b2af7"
integrity sha1-1Ztk6P7kHElFiqyCtWcYxZV7Kvc=
popper.js@^1.14.1:
version "1.16.1"
resolved "https://registry.yarnpkg.com/popper.js/-/popper.js-1.16.1.tgz#2a223cb3dc7b6213d740e40372be40de43e65b1b"

Loading…
Cancel
Save