diff --git a/src/database.js b/src/database.js index 6f6cc0a..62585f7 100644 --- a/src/database.js +++ b/src/database.js @@ -1,7 +1,7 @@ import React, { useEffect, useState, useContext } from 'react'; import NodeEnvironment from 'jest-environment-node'; -import { add_polygon_to_store, get_polygon_from_store } from './polygons.js'; +import { add_geo_area_to_store, get_geo_area_from_store } from './geo_store.js'; export async function sqljs_async_queries(sqljs_object, queries) { //var t0 = performance.now(); @@ -50,10 +50,10 @@ export function regexp_match(string, regex) { } export function is_in_geo_polygon_from_store(lat, long, polygon_hash) { - const geojson = get_polygon_from_store(polygon_hash); + const area = get_geo_area_from_store(polygon_hash); var gju = require('geojson-utils'); return gju.pointInPolygon({ "type": "Point", "coordinates": [long, lat] }, - geojson); + area.geojson); } // Digikam stores its tree of tags as individual tags, @@ -115,7 +115,7 @@ export function ProvideDB(props) { .then(db => { add_full_tag_info(db).then((newdb) => { db.create_function("REGEXP", regexp_match); - db.create_function("IS_IN_GEO_POLYGON", is_in_geo_polygon_from_store); + db.create_function("IS_IN_GEO", is_in_geo_polygon_from_store); setError(false); setDb(newdb); }) diff --git a/src/geo_store.js b/src/geo_store.js new file mode 100644 index 0000000..dab1de8 --- /dev/null +++ b/src/geo_store.js @@ -0,0 +1,22 @@ +var g_GeoStore = {}; + +export function hash_geo_area(geo_area) { + var hash = require('object-hash'); + return hash(geo_area); +} + +export function add_geo_area_to_store(area) { + var h = hash_geo_area(area); + if(!(h in g_GeoStore)) { + g_GeoStore[h] = area; + } + + return h; +} + +export function get_geo_area_from_store(hash) { + if(hash in g_GeoStore) { + return g_GeoStore[hash]; + } + throw new Error("Requested non-existent geo area from store."); +} \ No newline at end of file diff --git a/src/map.js b/src/map.js index 6a00eaf..dfb569e 100644 --- a/src/map.js +++ b/src/map.js @@ -3,8 +3,6 @@ import React, { useEffect, useState } from 'react'; import { makeStyles } from '@material-ui/core/styles'; import Box from '@material-ui/core/Box'; -import { SearchBar } from './searchbar.js'; - import "leaflet/dist/leaflet.css" import L from 'leaflet'; @@ -14,7 +12,7 @@ export function MapView(props) { // 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 { onMapChange, style } = props; const [map, setMap] = useState(null); const [camera, setCameraNoPush] = useState([51.505, -0.09, 13]); //lat, long, zoom @@ -60,16 +58,5 @@ export function MapView(props) { } }, [map]) - const style = { - width: "100%", - height: "600px", - }; - - return ( - <> - -
-
- - ); + return
} \ No newline at end of file diff --git a/src/polygons.js b/src/polygons.js deleted file mode 100644 index 476433c..0000000 --- a/src/polygons.js +++ /dev/null @@ -1,22 +0,0 @@ -var g_PolygonStore = {}; - -export function hash_polygon(polygon) { - var hash = require('object-hash'); - return hash(polygon); -} - -export function add_polygon_to_store(polygon) { - var h = hash_polygon(polygon); - if(!(h in g_PolygonStore)) { - g_PolygonStore[h] = polygon; - } - - return h; -} - -export function get_polygon_from_store(polygon_hash) { - if(polygon_hash in g_PolygonStore) { - return g_PolygonStore[polygon_hash]; - } - throw new Error("Requested non-existent polygon from store."); -} \ No newline at end of file diff --git a/src/queries.js b/src/queries.js index e13520c..f9eaf61 100644 --- a/src/queries.js +++ b/src/queries.js @@ -2,7 +2,7 @@ import { create_photo, create_album, create_tag } from './media.js'; import { sqljs_async_queries } from './database.js'; import { format } from 'date-fns'; -import { add_polygon_to_store } from './polygons.js'; +import { add_geo_area_to_store } from './geo_store.js'; export function escape_regex(s) { return s.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&'); @@ -303,10 +303,12 @@ export class LogicalOperatorFilter extends ResultFilter { export class LocationFilter extends ResultFilter { // The location filter always compares object locations (points) - // to a location polygon and filters objects outside said polygon. - constructor(rtype, polygon) { + // to a location and filters objects outside said location. + // Locations are directly stored from Nominatim API responses, + // which have GeoJSON and other metadata. + constructor(rtype, geo_area) { super(rtype); - this.polygon = polygon; + this.geo_area = geo_area; } to_sql_where() { @@ -316,9 +318,9 @@ export class LocationFilter extends ResultFilter { // by the custom comparison function invoked by SQL.js. // We need to store our polygon in said state and then return the query to guarantee that // it will have access to said polygon. - const hash = add_polygon_to_store(this.polygon); + const hash = add_geo_area_to_store(this.geo_area); return '(ImagePositions.latitudeNumber NOT NULL AND ImagePositions.longitudeNumber NOT NULL AND ' - + 'IS_IN_GEO_POLYGON(ImagePositions.latitudeNumber, ImagePositions.longitudeNumber, "' + hash + '"))'; + + 'IS_IN_GEO(ImagePositions.latitudeNumber, ImagePositions.longitudeNumber, "' + hash + '"))'; } simplify() { return this; } diff --git a/src/resultsview.js b/src/resultsview.js index 05c794c..4ce83d2 100644 --- a/src/resultsview.js +++ b/src/resultsview.js @@ -120,6 +120,11 @@ export function ResultsView(props) { const handleChange = (e, newindex) => { setActiveIndex(newindex); } + + const map_style = { + width: "100%", + height: "500px", + }; return (
@@ -148,7 +153,7 @@ export function ResultsView(props) { - +
diff --git a/src/userquerywidget.js b/src/userquerywidget.js index 96d8a9b..76efcaa 100644 --- a/src/userquerywidget.js +++ b/src/userquerywidget.js @@ -58,7 +58,7 @@ const useStyles = makeStyles(theme => ({ margin: "6px", }, mapcontainer: { - width: "600px", + width: "300px", }, })); @@ -72,25 +72,17 @@ export function EditLocationFilterExpression(props) { const _ = require('lodash'); function updateProposal(result) { - geoLayers.forEach(layer=> { + // Show the geometry proposed on the map. + geoLayers.forEach(layer => { map.removeLayer(layer); }); var layer = L.geoJSON(result.geojson).addTo(map); - map.flyToBounds(layer.getBounds()); + map.fitBounds(layer.getBounds()); setGeoLayers([layer]); - // // 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); - // result.geojson.coordinates[0].map(longlat => [longlat[1], longlat[0]]); - setProposal(result.geojson); + setProposal(result); } function onSearch(query) { @@ -101,7 +93,6 @@ export function EditLocationFilterExpression(props) { fetch("https://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]); } @@ -110,10 +101,15 @@ export function EditLocationFilterExpression(props) { function handleUseProposal() { var new_filter = _.cloneDeep(filter); - new_filter.polygon = proposal; + new_filter.geo_area = proposal; onChange(new_filter); } + const map_style = { + width: "300px", + height: "300px", + } + // 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. @@ -124,11 +120,11 @@ export function EditLocationFilterExpression(props) { - + - - Polygon with {filter.polygon.length} points. + + Using: {filter.geo_area.display_name} );