diff --git a/package.json b/package.json
index ce6206b..352e585 100644
--- a/package.json
+++ b/package.json
@@ -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",
diff --git a/src/database.js b/src/database.js
index 340d3fc..e5e83da 100644
--- a/src/database.js
+++ b/src/database.js
@@ -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,
diff --git a/src/map.js b/src/map.js
index 0874132..6a00eaf 100644
--- a/src/map.js
+++ b/src/map.js
@@ -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 (
<>
-
-
-
diff --git a/src/resultsview.js b/src/resultsview.js
index 0a95297..05c794c 100644
--- a/src/resultsview.js
+++ b/src/resultsview.js
@@ -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 (
diff --git a/src/userquerywidget.js b/src/userquerywidget.js
index 5688206..b71c122 100644
--- a/src/userquerywidget.js
+++ b/src/userquerywidget.js
@@ -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 (
- TODO
+ <>
+
+
+
+
+
+
+
+
+ Polygon with {filter.polygon.length} points.
+
+ >
);
}
diff --git a/yarn.lock b/yarn.lock
index 0acfb3f..a5764ac 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -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"