diff --git a/package.json b/package.json index 66d5af2..ce6206b 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,7 @@ "eslint-plugin-react-hooks": "^2.3.0", "leaflet": "^1.6.0", "lodash": "^4.17.15", + "object-hash": "^2.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 a9a1135..340d3fc 100644 --- a/src/database.js +++ b/src/database.js @@ -1,17 +1,19 @@ -import React, { useEffect, useState } from 'react'; +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'; + export async function sqljs_async_queries(sqljs_object, queries) { //var t0 = performance.now(); for (let i = 0; i < (queries.length - 1); i++) { - console.log("Query: ", queries[i]); + //console.log("Query: ", queries[i]); try { sqljs_object.exec(queries[i]); } catch (e) { throw e; } } - console.log("Query: ", queries[queries.length - 1]); + //console.log("Query: ", queries[queries.length - 1]); try { var r = sqljs_object.exec(queries[queries.length - 1]); } catch (e) { @@ -47,13 +49,13 @@ export function regexp_match(string, regex) { return string.match(regex) != null; } -export function is_in_geo_polygon(lat, long, polyid) { - return true; +export function is_in_polygon(x, y, poly) { + return false; } -export function add_custom_functions(db) { - db.create_function("REGEXP", regexp_match); - db.create_function("IS_IN_GEO_POLYGON", is_in_geo_polygon); +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); } // Digikam stores its tree of tags as individual tags, @@ -102,8 +104,6 @@ export async function add_full_tag_info(db) { db.exec(query); }); - console.log(db.exec("PRAGMA table_info([Tags]);")); - return db; } @@ -116,7 +116,8 @@ export function ProvideDB(props) { fetch_sqljs_db_from_sqlite(db_url) .then(db => { add_full_tag_info(db).then((newdb) => { - add_custom_functions(newdb); + db.create_function("REGEXP", regexp_match); + db.create_function("IS_IN_GEO_POLYGON", is_in_geo_polygon_from_store); setError(false); setDb(newdb); }) @@ -129,9 +130,5 @@ export function ProvideDB(props) { db_error: error, }; - if (db != null) { - console.log("Provided DB tags schema:", db.exec("PRAGMA table_info([Tags]);")); - } - return children({ ...child_props }); } \ No newline at end of file diff --git a/src/gridgallery.js b/src/gridgallery.js index a03b360..01c80ec 100644 --- a/src/gridgallery.js +++ b/src/gridgallery.js @@ -3,6 +3,8 @@ import Gallery from 'react-grid-gallery'; import { makeStyles } from '@material-ui/core/styles'; +// TODO: some nice options: ModuloBox, + const useStyles = makeStyles(theme => ({ root: { width: "100%", diff --git a/src/main.js b/src/main.js index ccf78c3..10a1704 100644 --- a/src/main.js +++ b/src/main.js @@ -12,7 +12,6 @@ import { UserQuery, user_query_from_search_string, maybe_image_query, do_image_q import { Browser } from './browser.js'; import { UserQueryWidget } from './userquerywidget.js'; import { ResultsView } from './resultsview.js'; -import { PolygonStoreProvider } from './polygons.js'; const useStyles = makeStyles(theme => ({ root: { @@ -126,18 +125,16 @@ export function LoadedMainPage(props) { return ( <> - - - - {albums && } - - - - - {photos && } - + + + {albums && } - + + + + {photos && } + + ); } diff --git a/src/map.js b/src/map.js index 67e9b22..0874132 100644 --- a/src/map.js +++ b/src/map.js @@ -58,7 +58,7 @@ export function MapView(props) { 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(jsonres); + //console.log("Nominatim result: ", jsonres); if (Array.isArray(jsonres) && jsonres.length > 0) { showNominatimResult(jsonres[0]); } diff --git a/src/polygons.js b/src/polygons.js index 2462584..476433c 100644 --- a/src/polygons.js +++ b/src/polygons.js @@ -1,21 +1,22 @@ -import React, { createContext, useReducer } from 'react'; +var g_PolygonStore = {}; -const polygonStore = createContext({}); -const { Provider } = polygonStore; +export function hash_polygon(polygon) { + var hash = require('object-hash'); + return hash(polygon); +} -const PolygonStoreProvider = ({ children }) => { - const [state, dispatch] = useReducer((state, action) => { - switch (action.type) { - case 'add polygon': - const newstate = state; - newstate[action.payload.id] = action.payload.data; - return newstate; - default: - throw new Error(); - }; - }, {}); +export function add_polygon_to_store(polygon) { + var h = hash_polygon(polygon); + if(!(h in g_PolygonStore)) { + g_PolygonStore[h] = polygon; + } - return {children}; -}; + return h; +} -export { polygonStore, PolygonStoreProvider } \ No newline at end of file +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 70ec079..d0a8bd3 100644 --- a/src/queries.js +++ b/src/queries.js @@ -2,6 +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'; export function escape_regex(s) { return s.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&'); @@ -12,7 +13,7 @@ export function do_image_query(query, database, collection_path, collection_thum var queries = []; queries.push(query); sqljs_async_queries(database, queries).then(res => { - console.log("response: ", res); + //console.log("Query result: ", res); var photos = []; if (res && Array.isArray(res) && res.length > 0) { var cols = res[0].columns; @@ -61,7 +62,6 @@ export function do_tag_query(query, database) { return new Promise(function (resolve, reject) { var queries = []; queries.push(query); - console.log("Provided DB tags schema before query:", database.exec("PRAGMA table_info([Tags]);")); sqljs_async_queries(database, queries).then(res => { var tags = []; if (res && Array.isArray(res) && res.length > 0) { @@ -136,7 +136,6 @@ export class NegationFilter extends ResultFilter { simplify() { var f = this.body.simplify(); - console.log("NOT body:", f); if (f.is_true()) { return new ConstFilter(this.result_type, false); } @@ -248,7 +247,6 @@ export class MatchingFilter extends ResultFilter { + '(\/[^\/]+)*"))'; } - console.log(this); throw new Error("Unsupported matching filter for SQL generation."); } simplify() { return this; } @@ -306,10 +304,9 @@ 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, polygon_store_dispatch) { + constructor(rtype, polygon) { super(rtype); this.polygon = polygon; - this.polygon_store_dispatch = polygon_store_dispatch; } to_sql_where() { @@ -319,13 +316,8 @@ 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 _ = require('lodash'); - const poly = { - id: _.uniqueId('polygon_'), - data: this.polygon - } - this.polygon_store_dispatch({ type: 'add polygon', payload: poly }); - return 'IS_IN_GEO_POLYGON(ImagePositions.latitude, ImagePositions.longitude, "' + poly.id + '")'; + const hash = add_polygon_to_store(this.polygon); + return 'IS_IN_GEO_POLYGON(ImagePositions.latitude, ImagePositions.longitude, "' + hash + '")'; } simplify() { return this; } diff --git a/src/userquerywidget.js b/src/userquerywidget.js index 5303eef..5688206 100644 --- a/src/userquerywidget.js +++ b/src/userquerywidget.js @@ -24,8 +24,6 @@ import { format } from 'date-fns'; import { makeStyles } from '@material-ui/core/styles'; -import { polygonStore } from './polygons.js'; - import { filter_is_const_false, ConstFilter, LogicalOperatorFilter, MatchingFilter, ResultTypeEnum, LogicalOperatorEnum, MatchTypeEnum, NegationFilter, TimeFilterTypeEnum, @@ -239,8 +237,6 @@ export function EditFilterExpressionDialog(props) { const { onClose, startingFilter, open } = props; const [filter, setFilter] = React.useState(startingFilter); - const polygons_dispatch = useContext(polygonStore).dispatch; - const FilterTypeEnum = { CONST: 0, NEGATION: 1, @@ -270,7 +266,7 @@ export function EditFilterExpressionDialog(props) { } else if (val == FilterTypeEnum.IMAGETYPE) { setFilter(new ImageTypeFilter(filter.result_type, ImageTypeEnum.PHOTO)); } else if (val == FilterTypeEnum.LOCATION) { - setFilter(new LocationFilter(filter.result_type, [[0.0, 0.0]], polygons_dispatch)); + setFilter(new LocationFilter(filter.result_type, [[0.0, 0.0]])); } else { throw new Error('Unsupported filter type: ' + val); } @@ -483,12 +479,12 @@ export function LogicalOperatorFilterExpressionControl(props) { - + - + @@ -532,13 +528,16 @@ export function NegationExpressionControl(props) { onChange(new_filter); } + // Only nodes which are transformation nodes may normally be removed. + const may_be_removed = expr instanceof NegationFilter; + return ( <> - + @@ -624,7 +623,7 @@ export function LocationFilterExpressionControl(props) { } export function FilterExpressionControl(props) { - const { expr, onChange, isRoot } = props; + const { expr, onChange, mayBeRemoved } = props; const [anchorEl, setAnchorEl] = React.useState(null); const [editDialogOpen, setEditDialogOpen] = React.useState(false); const [combineDialogOpen, setCombineDialogOpen] = React.useState(false); @@ -718,11 +717,6 @@ export function FilterExpressionControl(props) { throw new Error('Unsupported filter expression'); } - // If this is the root node, removing it is not allowed. - // Other nodes may be removed. - // The only exception is a negation node: removing that will replace it by its child. - var allowRemove = !isRoot || (expr instanceof NegationFilter); - return ( <> {filter_elem} @@ -737,7 +731,7 @@ export function FilterExpressionControl(props) { And... Or... Negate - {allowRemove && Remove} + {mayBeRemoved && Remove} @@ -781,7 +779,7 @@ export function FilterControl(props) { } label={resultTypeString + ':'} /> - + ); diff --git a/yarn.lock b/yarn.lock index 3728018..0acfb3f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7252,6 +7252,11 @@ object-hash@^1.3.1: resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-1.3.1.tgz#fde452098a951cb145f039bb7d455449ddc126df" integrity sha512-OSuu/pU4ENM9kmREg0BdNrUDIl1heYa4mBZacJc+vVWz4GtAwu7jO8s4AIt2aGRUTqxykpWzI3Oqnsm13tTMDA== +object-hash@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-2.0.1.tgz#cef18a0c940cc60aa27965ecf49b782cbf101d96" + integrity sha512-HgcGMooY4JC2PBt9sdUdJ6PMzpin+YtY3r/7wg0uTifP+HJWW8rammseSEHuyt0UeShI183UGssCJqm1bJR7QA== + object-inspect@^1.7.0: version "1.7.0" resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.7.0.tgz#f4f6bd181ad77f006b5ece60bd0b6f398ff74a67"