diff --git a/src/essence/Ancillary/LocalFilterer.js b/src/essence/Ancillary/LocalFilterer.js
index 38bce342..e110b28e 100644
--- a/src/essence/Ancillary/LocalFilterer.js
+++ b/src/essence/Ancillary/LocalFilterer.js
@@ -115,7 +115,7 @@ const LocalFilterer = {
refreshFunction(filteredGeoJSON)
} else {
L_.clearVectorLayer(layerName)
- L_.updateVectorLayer(layerName, filteredGeoJSON)
+ L_.updateVectorLayer(layerName, filteredGeoJSON, null, true)
}
},
match: function (feature, filter) {
diff --git a/src/essence/Tools/Layers/Filtering/ESFilterer.js b/src/essence/Basics/Layers_/Filtering/ESFilterer.js
similarity index 96%
rename from src/essence/Tools/Layers/Filtering/ESFilterer.js
rename to src/essence/Basics/Layers_/Filtering/ESFilterer.js
index 44db25b8..c42c7a7e 100644
--- a/src/essence/Tools/Layers/Filtering/ESFilterer.js
+++ b/src/essence/Basics/Layers_/Filtering/ESFilterer.js
@@ -1,245 +1,245 @@
-// Part of the LayersTool that deals with filtering
-
-import $ from 'jquery'
-import F_ from '../../../Basics/Formulae_/Formulae_'
-import L_ from '../../../Basics/Layers_/Layers_'
-
-const ESFilterer = {
- getAggregations: async function (layerName, config) {
- const results = await ESFilterer.filter(layerName, null, config)
-
- const aggs = {}
- if (results) {
- for (let a in results.aggregations) {
- const flatBuckets = {}
- results.aggregations[a].buckets.forEach((b) => {
- flatBuckets[b.key] = b.doc_count
- })
-
- aggs[a] = {
- aggs: flatBuckets,
- type: 'string',
- }
- }
- }
- return aggs
- },
- filter: async function (layerName, filter, config) {
- return new Promise((resolve, reject) => {
- let aggs = {}
- config.fields = config.fields || {}
- for (let f in config.fields) {
- aggs[f] = {
- terms: {
- field: f,
- size: config.fields[f] || 10,
- },
- }
- }
-
- let must = []
- if (config.must) must = must.concat(config.must)
- const filterMust = ESFilterer.getFilterMust(filter)
- must = must.concat(filterMust)
-
- let query = {
- query: {
- bool: {
- must: must,
- },
- },
- aggs: aggs,
- from: 0,
- size: filter == null ? 0 : config.size || 500,
- version: true,
- }
-
- if (config.collapse)
- query.collapse = {
- field: config.collapse,
- }
-
- if (config.sort) query.sort = config.sort
-
- // Spatial Filter
- let spatialFilter
- if (
- config.geoshapeProp &&
- filter?.spatial?.center != null &&
- filter?.spatial?.feature?.geometry?.type != null
- ) {
- if (filter.spatial.radius > 0) {
- spatialFilter = {
- geo_shape: {
- [config.geoshapeProp]: {
- shape: {
- type: filter.spatial.feature.geometry.type.toLowerCase(),
- coordinates:
- filter.spatial.feature.geometry
- .coordinates,
- relation: 'intersects',
- },
- },
- },
- }
- } else {
- spatialFilter = {
- geo_shape: {
- [config.geoshapeProp]: {
- shape: {
- type: 'point',
- coordinates: [
- filter.spatial.center.lng,
- filter.spatial.center.lat,
- ],
- relation: 'contains',
- },
- },
- },
- }
- }
- }
- if (spatialFilter) {
- query.query.bool.filter = spatialFilter
- }
-
- // Ignore results if no filters set
- if (spatialFilter == null && filterMust.length === 0) {
- query.size = 0
- }
-
- // Format query
- let body = query
- if (config.stringifyBody) body = JSON.stringify(body)
-
- let finalBody
- if (config.bodyWrapper)
- finalBody = config.bodyWrapper.replace('{BODY}', body)
- else finalBody = body
-
- // Fetch
- const resp = config.esResponses ? 'responses.' : ''
-
- fetch(
- `${config.endpoint}?filter_path=${resp}hits.hits._source,${resp}hits.total,${resp}aggregations`,
- {
- method: 'POST',
- headers: {
- accept: 'application/json',
- ...(config.headers || {}),
- },
- credentials: config.withCredentials ? 'include' : '',
- body: finalBody,
- }
- )
- .then((res) => res.json())
- .then((json) => {
- const geojson = F_.getBaseGeoJSON()
- const hits = F_.getIn(json, 'responses.0.hits.hits', [])
-
- hits.forEach((hit) => {
- const properties = hit._source || {}
- let geometry
- for (let p in properties) {
- if (
- properties[p] != null &&
- typeof properties[p] === 'object' &&
- properties[p].coordinates &&
- properties[p].type
- ) {
- geometry = JSON.parse(
- JSON.stringify(properties[p])
- )
- delete properties[p]
- break
- }
- }
- if (geometry)
- geojson.features.push({
- type: 'Feature',
- properties,
- geometry,
- })
- })
- // Set count
- $('#layersTool_filtering_count').text(
- `(${geojson.features.length} out of ${F_.getIn(
- json,
- config.esResponses
- ? 'responses.0.hits.total.value'
- : 'hits.total.value',
- 0
- )})`
- )
-
- // Update layer
- L_.clearVectorLayer(layerName)
- L_.updateVectorLayer(layerName, geojson)
-
- resolve(
- config.esResponses
- ? F_.getIn(json, 'responses.0')
- : json
- )
- })
- .catch((err) => {
- console.log(err)
- resolve()
- })
- })
- },
- getFilterMust: function (filter) {
- let must = []
- if (filter && filter.values && filter.values.length > 0) {
- filter.values.forEach((v) => {
- if (v == null || v.key == null || v.value == null) return
- switch (v.op) {
- case '=':
- must.push({
- match: {
- [v.key]: v.value,
- },
- })
- break
- case ',':
- const stringValue = v.value + ''
- must.push({
- bool: {
- should: stringValue.split(',').map((sv) => {
- return {
- match: {
- [v.key]: sv,
- },
- }
- }),
- },
- })
- break
- case '>':
- must.push({
- range: {
- [v.key]: {
- gt: v.value,
- },
- },
- })
- break
- case '<':
- must.push({
- range: {
- [v.key]: {
- lt: v.value,
- },
- },
- })
- break
- default:
- break
- }
- })
- }
- return must
- },
-}
-
-export default ESFilterer
+// Part of the LayersTool that deals with filtering
+
+import $ from 'jquery'
+import F_ from '../../Formulae_/Formulae_'
+import L_ from '../../Layers_/Layers_'
+
+const ESFilterer = {
+ getAggregations: async function (layerName, config) {
+ const results = await ESFilterer.filter(layerName, null, config)
+
+ const aggs = {}
+ if (results) {
+ for (let a in results.aggregations) {
+ const flatBuckets = {}
+ results.aggregations[a].buckets.forEach((b) => {
+ flatBuckets[b.key] = b.doc_count
+ })
+
+ aggs[a] = {
+ aggs: flatBuckets,
+ type: 'string',
+ }
+ }
+ }
+ return aggs
+ },
+ filter: async function (layerName, filter, config) {
+ return new Promise((resolve, reject) => {
+ let aggs = {}
+ config.fields = config.fields || {}
+ for (let f in config.fields) {
+ aggs[f] = {
+ terms: {
+ field: f,
+ size: config.fields[f] || 10,
+ },
+ }
+ }
+
+ let must = []
+ if (config.must) must = must.concat(config.must)
+ const filterMust = ESFilterer.getFilterMust(filter)
+ must = must.concat(filterMust)
+
+ let query = {
+ query: {
+ bool: {
+ must: must,
+ },
+ },
+ aggs: aggs,
+ from: 0,
+ size: filter == null ? 0 : config.size || 500,
+ version: true,
+ }
+
+ if (config.collapse)
+ query.collapse = {
+ field: config.collapse,
+ }
+
+ if (config.sort) query.sort = config.sort
+
+ // Spatial Filter
+ let spatialFilter
+ if (
+ config.geoshapeProp &&
+ filter?.spatial?.center != null &&
+ filter?.spatial?.feature?.geometry?.type != null
+ ) {
+ if (filter.spatial.radius > 0) {
+ spatialFilter = {
+ geo_shape: {
+ [config.geoshapeProp]: {
+ shape: {
+ type: filter.spatial.feature.geometry.type.toLowerCase(),
+ coordinates:
+ filter.spatial.feature.geometry
+ .coordinates,
+ relation: 'intersects',
+ },
+ },
+ },
+ }
+ } else {
+ spatialFilter = {
+ geo_shape: {
+ [config.geoshapeProp]: {
+ shape: {
+ type: 'point',
+ coordinates: [
+ filter.spatial.center.lng,
+ filter.spatial.center.lat,
+ ],
+ relation: 'contains',
+ },
+ },
+ },
+ }
+ }
+ }
+ if (spatialFilter) {
+ query.query.bool.filter = spatialFilter
+ }
+
+ // Ignore results if no filters set
+ if (spatialFilter == null && filterMust.length === 0) {
+ query.size = 0
+ }
+
+ // Format query
+ let body = query
+ if (config.stringifyBody) body = JSON.stringify(body)
+
+ let finalBody
+ if (config.bodyWrapper)
+ finalBody = config.bodyWrapper.replace('{BODY}', body)
+ else finalBody = body
+
+ // Fetch
+ const resp = config.esResponses ? 'responses.' : ''
+
+ fetch(
+ `${config.endpoint}?filter_path=${resp}hits.hits._source,${resp}hits.total,${resp}aggregations`,
+ {
+ method: 'POST',
+ headers: {
+ accept: 'application/json',
+ ...(config.headers || {}),
+ },
+ credentials: config.withCredentials ? 'include' : '',
+ body: finalBody,
+ }
+ )
+ .then((res) => res.json())
+ .then((json) => {
+ const geojson = F_.getBaseGeoJSON()
+ const hits = F_.getIn(json, 'responses.0.hits.hits', [])
+
+ hits.forEach((hit) => {
+ const properties = hit._source || {}
+ let geometry
+ for (let p in properties) {
+ if (
+ properties[p] != null &&
+ typeof properties[p] === 'object' &&
+ properties[p].coordinates &&
+ properties[p].type
+ ) {
+ geometry = JSON.parse(
+ JSON.stringify(properties[p])
+ )
+ delete properties[p]
+ break
+ }
+ }
+ if (geometry)
+ geojson.features.push({
+ type: 'Feature',
+ properties,
+ geometry,
+ })
+ })
+ // Set count
+ $('#layersTool_filtering_count').text(
+ `(${geojson.features.length} out of ${F_.getIn(
+ json,
+ config.esResponses
+ ? 'responses.0.hits.total.value'
+ : 'hits.total.value',
+ 0
+ )})`
+ )
+
+ // Update layer
+ L_.clearVectorLayer(layerName)
+ L_.updateVectorLayer(layerName, geojson)
+
+ resolve(
+ config.esResponses
+ ? F_.getIn(json, 'responses.0')
+ : json
+ )
+ })
+ .catch((err) => {
+ console.log(err)
+ resolve()
+ })
+ })
+ },
+ getFilterMust: function (filter) {
+ let must = []
+ if (filter && filter.values && filter.values.length > 0) {
+ filter.values.forEach((v) => {
+ if (v == null || v.key == null || v.value == null) return
+ switch (v.op) {
+ case '=':
+ must.push({
+ match: {
+ [v.key]: v.value,
+ },
+ })
+ break
+ case ',':
+ const stringValue = v.value + ''
+ must.push({
+ bool: {
+ should: stringValue.split(',').map((sv) => {
+ return {
+ match: {
+ [v.key]: sv,
+ },
+ }
+ }),
+ },
+ })
+ break
+ case '>':
+ must.push({
+ range: {
+ [v.key]: {
+ gt: v.value,
+ },
+ },
+ })
+ break
+ case '<':
+ must.push({
+ range: {
+ [v.key]: {
+ lt: v.value,
+ },
+ },
+ })
+ break
+ default:
+ break
+ }
+ })
+ }
+ return must
+ },
+}
+
+export default ESFilterer
diff --git a/src/essence/Tools/Layers/Filtering/Filtering.css b/src/essence/Basics/Layers_/Filtering/Filtering.css
similarity index 95%
rename from src/essence/Tools/Layers/Filtering/Filtering.css
rename to src/essence/Basics/Layers_/Filtering/Filtering.css
index abbf0dff..e8604a1a 100644
--- a/src/essence/Tools/Layers/Filtering/Filtering.css
+++ b/src/essence/Basics/Layers_/Filtering/Filtering.css
@@ -1,289 +1,289 @@
-#layersTool_filtering {
- border-top: 1px solid var(--color-a);
- background: var(--color-a2);
- display: none;
-}
-.gears_on #layersTool_filtering {
- display: block;
-}
-#layersTool_filtering_header {
- display: flex;
- justify-content: space-between;
- height: 30px;
- line-height: 30px;
-}
-#layersTool_filtering_title_left {
- display: flex;
-}
-#layersTool_filtering_title {
- font-size: 16px;
- padding-left: 14px;
- line-height: 31px;
-}
-#layersTool_filtering_count {
- font-size: 12px;
- padding: 0px 4px;
- color: #d6d6d6;
-}
-#layersTool_filtering_add_value {
- padding: 0px 6px;
- color: var(--color-a6);
-}
-#layersTool_filtering_add_value:hover {
- background: var(--color-c);
- color: var(--color-a7);
-}
-#layersTool_filtering_add_value > div {
- font-size: 11px;
- margin-right: 2px;
- line-height: 32px;
- text-transform: uppercase;
-}
-
-#layerTool_filtering_filters {
- border-left: 1px solid var(--color-a2);
- border-right: 1px solid var(--color-a2);
-}
-
-#layerTool_filtering_filters_spatial {
- display: flex;
- border-bottom: 1px solid #353535;
-}
-#layerTool_filtering_filters_spatial_draw {
- flex: 1;
- display: flex;
- overflow: hidden;
- white-space: nowrap;
- padding-left: 0px;
- margin: 4px 8px;
- height: 22px;
- line-height: 22px;
- border-radius: 4px;
- background: var(--color-a3);
-}
-#layerTool_filtering_filters_spatial_draw > div {
- line-height: 24px;
- font-size: 13px;
-}
-#layerTool_filtering_filters_spatial_draw > i {
- color: var(--color-a6);
- width: 22px;
- height: 22px;
- line-height: 24px;
- border-top-left-radius: 4px;
- border-bottom-left-radius: 4px;
- padding-left: 2px;
- margin-right: 6px;
-}
-#layerTool_filtering_filters_spatial.drawing
- #layerTool_filtering_filters_spatial_draw
- > i {
- background: var(--color-r7);
- color: black;
-}
-#layerTool_filtering_filters_spatial.drawn
- #layerTool_filtering_filters_spatial_draw
- > i {
- background: var(--color-green2);
- color: black;
-}
-#layerTool_filtering_filters_spatial_draw:hover {
- background: var(--color-c);
-}
-.layerTool_filtering_filters_clear {
- width: 30px;
- background: var(--color-a1-5);
- padding: 0px 6px;
- color: #888;
-}
-.layerTool_filtering_filters_clear:hover {
- background: var(--color-a1);
- color: #fff;
-}
-#layerTool_filtering_filters_spatial_radius_wrapper {
- display: flex;
- background: var(--color-a1-5);
- color: #ccc;
- width: 131px;
- box-sizing: border-box;
- border-right: 1px solid var(--color-a2);
-}
-#layersTool_filtering
- #layerTool_filtering_filters_spatial_radius_wrapper
- > input {
- background: var(--color-a1-5);
- padding-left: 0px;
- text-align: right;
- font-size: 13px;
-}
-#layersTool_filtering
- #layerTool_filtering_filters_spatial_radius_wrapper
- > input:hover {
- background: var(--color-a1-5);
-}
-#layerTool_filtering_filters_spatial_radius_wrapper > div:first-child {
- line-height: 30px;
- padding: 0px 8px;
- font-size: 12px;
-}
-#layerTool_filtering_filters_spatial_radius_wrapper > div:last-child {
- line-height: 32px;
- height: 30px;
- padding: 0px 8px 0px 1px;
- font-size: 12px;
-}
-
-#layersTool_filtering_footer {
- display: flex;
- justify-content: space-between;
- height: 30px;
- line-height: 30px;
-}
-#layersTool_filtering_adds {
- display: flex;
- justify-content: space-between;
-}
-#layersTool_filtering_adds > div > div {
- padding-left: 2px;
-}
-
-.layersTool_filtering_value {
- display: flex;
- height: 31px;
- border-bottom: 1px solid var(--color-a2);
-}
-.layersTool_filtering_value .layersTool_filtering_value_key {
- flex: 1;
-}
-.layersTool_filtering_value_operator {
- box-sizing: border-box;
- border-left: 1px solid var(--color-a2);
- border-right: 1px solid var(--color-a2);
-}
-.layersTool_filtering_value .layersTool_filtering_value_value {
- flex: 1;
- position: relative;
-}
-
-.layersTool_filtering_value_value_type {
- position: absolute;
- right: 0;
- top: 0;
- pointer-events: none;
- height: 30px;
- line-height: 30px;
- width: 30px;
- text-align: center;
- color: #999;
- opacity: 0;
- transition: opacity 0.2s ease-out;
-}
-.layersTool_filtering_value_value_type > i:first-child {
- color: var(--color-yellow);
-}
-.layersTool_filtering_value_value_type > i:last-child {
- color: var(--color-green);
-}
-
-.layersTool_filtering_value_value_input:hover
- + .layersTool_filtering_value_value_type {
- opacity: 1;
-}
-
-#layersTool_filtering input {
- background: var(--color-a1-5);
- color: #ccc;
- width: 100%;
- box-sizing: border-box;
- border-left: 6px solid var(--color-a);
- height: 30px;
- padding-left: 8px;
- border: none;
- font-size: 14px;
- transition: background 0.2s cubic-bezier(0.785, 0.135, 0.15, 0.86);
-}
-#layersTool_filtering input:hover {
- background: var(--color-a1);
-}
-.layersTool_filtering_value_value_input {
- border-right: 1px solid var(--color-a2) !important;
-}
-#layersTool_filtering select {
- background: #111;
- color: #ccc;
- height: 30px;
-}
-.layersTool_filtering_value_operator_select {
- background: var(--color-a1);
- color: var(--color-f);
- border: none;
- cursor: pointer;
- height: 30px;
- transition: background 0.2s cubic-bezier(0.785, 0.135, 0.15, 0.86);
-}
-.layersTool_filtering_value_operator_select:hover {
- background: var(--color-a2);
-}
-
-.layersTool_filtering_value_remove .mmgisButton5 {
- width: 22px;
-}
-#layersTool_filtering_clear {
- padding: 0px 14px;
-}
-#layersTool_filtering_clear:hover {
- background: var(--color-red2);
-}
-#layersTool_filtering_submit {
- padding: 0px 8px;
- transition: background 0.2s cubic-bezier(0.785, 0.135, 0.15, 0.86);
-}
-#layersTool_filtering_submit.active {
- background: var(--color-h);
- color: black;
-}
-#layersTool_filtering_submit.active:hover {
- background: #fffb80;
-}
-
-#layersTool_filtering .dropy,
-#layersTool_filtering .dropy__title {
- height: 30px;
- border: none;
-}
-#layersTool_filtering .dropy__title span {
- padding: 6px;
-}
-#layersTool_filtering .dropy .dropy__content li a {
- padding: 6px;
- border-top: 1px solid var(--color-m);
- margin-bottom: 0px;
- height: 30px;
-}
-#layersTool_filtering .dropy__content .dropy__header {
- display: none;
-}
-#layersTool_filtering .dropy__content ul {
- transition: none;
-}
-
-#layersTool_filtering_submit_loading {
- display: none;
- padding: 6px 6px 6px 0px;
-}
-#layersTool_filtering_submit_loading.active {
- display: block;
-}
-#layersTool_filtering_submit_loading.active > div {
- width: 18px;
- height: 18px;
- border-radius: 50%;
- border: 3px solid black;
- border-top: 3px solid transparent;
- animation: submitLoadingSpin 1s ease-in-out infinite;
-}
-@keyframes submitLoadingSpin {
- to {
- transform: rotate(360deg);
- }
-}
+#layersTool_filtering {
+ border-top: 1px solid var(--color-a);
+ background: var(--color-a2);
+ display: none;
+}
+.gears_on #layersTool_filtering {
+ display: block;
+}
+#layersTool_filtering_header {
+ display: flex;
+ justify-content: space-between;
+ height: 30px;
+ line-height: 30px;
+}
+#layersTool_filtering_title_left {
+ display: flex;
+}
+#layersTool_filtering_title {
+ font-size: 16px;
+ padding-left: 14px;
+ line-height: 31px;
+}
+#layersTool_filtering_count {
+ font-size: 12px;
+ padding: 0px 4px;
+ color: #d6d6d6;
+}
+#layersTool_filtering_add_value {
+ padding: 0px 6px;
+ color: var(--color-a6);
+}
+#layersTool_filtering_add_value:hover {
+ background: var(--color-c);
+ color: var(--color-a7);
+}
+#layersTool_filtering_add_value > div {
+ font-size: 11px;
+ margin-right: 2px;
+ line-height: 32px;
+ text-transform: uppercase;
+}
+
+#layerTool_filtering_filters {
+ border-left: 1px solid var(--color-a2);
+ border-right: 1px solid var(--color-a2);
+}
+
+#layerTool_filtering_filters_spatial {
+ display: flex;
+ border-bottom: 1px solid #353535;
+}
+#layerTool_filtering_filters_spatial_draw {
+ flex: 1;
+ display: flex;
+ overflow: hidden;
+ white-space: nowrap;
+ padding-left: 0px;
+ margin: 4px 8px;
+ height: 22px;
+ line-height: 22px;
+ border-radius: 4px;
+ background: var(--color-a3);
+}
+#layerTool_filtering_filters_spatial_draw > div {
+ line-height: 24px;
+ font-size: 13px;
+}
+#layerTool_filtering_filters_spatial_draw > i {
+ color: var(--color-a6);
+ width: 22px;
+ height: 22px;
+ line-height: 24px;
+ border-top-left-radius: 4px;
+ border-bottom-left-radius: 4px;
+ padding-left: 2px;
+ margin-right: 6px;
+}
+#layerTool_filtering_filters_spatial.drawing
+ #layerTool_filtering_filters_spatial_draw
+ > i {
+ background: var(--color-r7);
+ color: black;
+}
+#layerTool_filtering_filters_spatial.drawn
+ #layerTool_filtering_filters_spatial_draw
+ > i {
+ background: var(--color-green2);
+ color: black;
+}
+#layerTool_filtering_filters_spatial_draw:hover {
+ background: var(--color-c);
+}
+.layerTool_filtering_filters_clear {
+ width: 30px;
+ background: var(--color-a1-5);
+ padding: 0px 6px;
+ color: #888;
+}
+.layerTool_filtering_filters_clear:hover {
+ background: var(--color-a1);
+ color: #fff;
+}
+#layerTool_filtering_filters_spatial_radius_wrapper {
+ display: flex;
+ background: var(--color-a1-5);
+ color: #ccc;
+ width: 131px;
+ box-sizing: border-box;
+ border-right: 1px solid var(--color-a2);
+}
+#layersTool_filtering
+ #layerTool_filtering_filters_spatial_radius_wrapper
+ > input {
+ background: var(--color-a1-5);
+ padding-left: 0px;
+ text-align: right;
+ font-size: 13px;
+}
+#layersTool_filtering
+ #layerTool_filtering_filters_spatial_radius_wrapper
+ > input:hover {
+ background: var(--color-a1-5);
+}
+#layerTool_filtering_filters_spatial_radius_wrapper > div:first-child {
+ line-height: 30px;
+ padding: 0px 8px;
+ font-size: 12px;
+}
+#layerTool_filtering_filters_spatial_radius_wrapper > div:last-child {
+ line-height: 32px;
+ height: 30px;
+ padding: 0px 8px 0px 1px;
+ font-size: 12px;
+}
+
+#layersTool_filtering_footer {
+ display: flex;
+ justify-content: space-between;
+ height: 30px;
+ line-height: 30px;
+}
+#layersTool_filtering_adds {
+ display: flex;
+ justify-content: space-between;
+}
+#layersTool_filtering_adds > div > div {
+ padding-left: 2px;
+}
+
+.layersTool_filtering_value {
+ display: flex;
+ height: 31px;
+ border-bottom: 1px solid var(--color-a2);
+}
+.layersTool_filtering_value .layersTool_filtering_value_key {
+ flex: 1;
+}
+.layersTool_filtering_value_operator {
+ box-sizing: border-box;
+ border-left: 1px solid var(--color-a2);
+ border-right: 1px solid var(--color-a2);
+}
+.layersTool_filtering_value .layersTool_filtering_value_value {
+ flex: 1;
+ position: relative;
+}
+
+.layersTool_filtering_value_value_type {
+ position: absolute;
+ right: 0;
+ top: 0;
+ pointer-events: none;
+ height: 30px;
+ line-height: 30px;
+ width: 30px;
+ text-align: center;
+ color: #999;
+ opacity: 0;
+ transition: opacity 0.2s ease-out;
+}
+.layersTool_filtering_value_value_type > i:first-child {
+ color: var(--color-yellow);
+}
+.layersTool_filtering_value_value_type > i:last-child {
+ color: var(--color-green);
+}
+
+.layersTool_filtering_value_value_input:hover
+ + .layersTool_filtering_value_value_type {
+ opacity: 1;
+}
+
+#layersTool_filtering input {
+ background: var(--color-a1-5);
+ color: #ccc;
+ width: 100%;
+ box-sizing: border-box;
+ border-left: 6px solid var(--color-a);
+ height: 30px;
+ padding-left: 8px;
+ border: none;
+ font-size: 14px;
+ transition: background 0.2s cubic-bezier(0.785, 0.135, 0.15, 0.86);
+}
+#layersTool_filtering input:hover {
+ background: var(--color-a1);
+}
+.layersTool_filtering_value_value_input {
+ border-right: 1px solid var(--color-a2) !important;
+}
+#layersTool_filtering select {
+ background: #111;
+ color: #ccc;
+ height: 30px;
+}
+.layersTool_filtering_value_operator_select {
+ background: var(--color-a1);
+ color: var(--color-f);
+ border: none;
+ cursor: pointer;
+ height: 30px;
+ transition: background 0.2s cubic-bezier(0.785, 0.135, 0.15, 0.86);
+}
+.layersTool_filtering_value_operator_select:hover {
+ background: var(--color-a2);
+}
+
+.layersTool_filtering_value_remove .mmgisButton5 {
+ width: 22px;
+}
+#layersTool_filtering_clear {
+ padding: 0px 14px;
+}
+#layersTool_filtering_clear:hover {
+ background: var(--color-red2);
+}
+#layersTool_filtering_submit {
+ padding: 0px 8px;
+ transition: background 0.2s cubic-bezier(0.785, 0.135, 0.15, 0.86);
+}
+#layersTool_filtering_submit.active {
+ background: var(--color-h);
+ color: black;
+}
+#layersTool_filtering_submit.active:hover {
+ background: #fffb80;
+}
+
+#layersTool_filtering .dropy,
+#layersTool_filtering .dropy__title {
+ height: 30px;
+ border: none;
+}
+#layersTool_filtering .dropy__title span {
+ padding: 6px;
+}
+#layersTool_filtering .dropy .dropy__content li a {
+ padding: 6px;
+ border-top: 1px solid var(--color-m);
+ margin-bottom: 0px;
+ height: 30px;
+}
+#layersTool_filtering .dropy__content .dropy__header {
+ display: none;
+}
+#layersTool_filtering .dropy__content ul {
+ transition: none;
+}
+
+#layersTool_filtering_submit_loading {
+ display: none;
+ padding: 6px 6px 6px 0px;
+}
+#layersTool_filtering_submit_loading.active {
+ display: block;
+}
+#layersTool_filtering_submit_loading.active > div {
+ width: 18px;
+ height: 18px;
+ border-radius: 50%;
+ border: 3px solid black;
+ border-top: 3px solid transparent;
+ animation: submitLoadingSpin 1s ease-in-out infinite;
+}
+@keyframes submitLoadingSpin {
+ to {
+ transform: rotate(360deg);
+ }
+}
diff --git a/src/essence/Tools/Layers/Filtering/Filtering.js b/src/essence/Basics/Layers_/Filtering/Filtering.js
similarity index 92%
rename from src/essence/Tools/Layers/Filtering/Filtering.js
rename to src/essence/Basics/Layers_/Filtering/Filtering.js
index 2d0867cb..7dfebea9 100644
--- a/src/essence/Tools/Layers/Filtering/Filtering.js
+++ b/src/essence/Basics/Layers_/Filtering/Filtering.js
@@ -1,611 +1,656 @@
-// Part of the LayersTool that deals with filtering
-
-import $ from 'jquery'
-import F_ from '../../../Basics/Formulae_/Formulae_'
-import L_ from '../../../Basics/Layers_/Layers_'
-import Map_ from '../../../Basics/Map_/Map_'
-
-import LocalFilterer from '../../../Ancillary/LocalFilterer'
-import ESFilterer from './ESFilterer'
-
-import Help from '../../../Ancillary/Help'
-import Dropy from '../../../../external/Dropy/dropy'
-import { circle } from '@turf/turf'
-
-import './Filtering.css'
-
-const helpKey = 'LayersTool-Filtering'
-
-const Filtering = {
- filters: {},
- current: {},
- mapSpatialLayer: null,
- make: async function (container, layerName) {
- const layerObj = L_.layers.data[layerName]
-
- if (layerObj == null) return
-
- Filtering.filters[layerName] = Filtering.filters[layerName] || {
- spatial: {
- center: null,
- radius: 0,
- },
- values: [],
- geojson: null,
- }
- Filtering.current = {
- layerName: layerName,
- layerObj: layerObj,
- type: layerObj.type,
- }
-
- if (Filtering.current.type === 'vector') {
- try {
- Filtering.filters[layerName].geojson =
- Filtering.filters[layerName].geojson ||
- L_.layers.layer[layerName].toGeoJSON(L_.GEOJSON_PRECISION)
- } catch (err) {
- console.warn(
- `Filtering - Cannot find GeoJSON to filter on for layer: ${layerName}`
- )
- return
- }
- Filtering.filters[layerName].aggs = LocalFilterer.getAggregations(
- Filtering.filters[layerName].geojson
- )
- } else if (Filtering.current.type === 'query') {
- Filtering.filters[layerName].aggs =
- await ESFilterer.getAggregations(
- layerName,
- Filtering.getConfig()
- )
- }
- const spatialActive =
- Filtering.filters[layerName].spatial?.center != null
-
- // prettier-ignore
- const markup = [
- "
",
- ].join('\n')
-
- container.append(markup)
-
- Filtering.filters[layerName].values.forEach((v) => {
- if (v) Filtering.addValue(layerName, v)
- })
-
- Filtering.attachEvents(layerName)
-
- Filtering.drawSpatialLayer(
- layerName,
- Filtering.filters[layerName].spatial.center,
- Filtering.filters[layerName].spatial.radius
- )
-
- // Start with one empty row added
- if (
- $('#layerTool_filtering_filters_list .layersTool_filtering_value')
- .length === 0
- )
- Filtering.addValue(layerName)
-
- Help.finalize(helpKey)
- },
- destroy: function () {
- // Clear Spatial Filter
- Map_.rmNotNull(Filtering.mapSpatialLayer)
-
- $('#layersTool_filtering').remove()
- },
- addValue: function (layerName, value) {
- let id, key, op, val
- if (value) {
- id = value.id
- key = value.key != null ? ` value='${value.key}'` : ''
- op = value.op
- val = value.value != null ? ` value='${value.value}'` : ''
- } else id = Filtering.filters[layerName].values.length
-
- // prettier-ignore
- const valueMarkup = [
- `",
- ].join('\n')
-
- $('#layerTool_filtering_filters_list').append(valueMarkup)
-
- if (value == null) {
- Filtering.filters[layerName].values.push({
- id: id,
- type: null,
- key: null,
- op: '=',
- value: null,
- })
- }
-
- Filtering.attachValueEvents(id, layerName, { op: op })
-
- // Show footer iff value rows exist
- $('#layersTool_filtering_footer').css(
- 'display',
- Filtering.filters[layerName].values.length === 0 ? 'none' : 'flex'
- )
- },
- drawSpatialLayer: function (layerName, center, radius) {
- Map_.rmNotNull(Filtering.mapSpatialLayer)
-
- Filtering.setSubmitButtonState(true)
- if (center == null) return
-
- const style = {
- fillOpacity: 0.1,
- fillColor: 'white',
- color: 'lime',
- weight: 2,
- opacity: 1,
- className: 'noPointerEventsImportant',
- }
-
- if (radius > 0) {
- // Buffered Circle
- const geojson = F_.getBaseGeoJSON()
- geojson.features.push(
- circle(
- [center.lng, center.lat],
- radius * 0.001 * F_.getEarthToPlanetRatio()
- )
- )
-
- Filtering.mapSpatialLayer = L.geoJSON(geojson, {
- style: style,
- }).addTo(Map_.map)
- Filtering.filters[layerName].spatial.feature = geojson.features[0]
- } else {
- // Circle marker
- Filtering.mapSpatialLayer = new L.circleMarker(
- [center.lat, center.lng],
- style
- )
- .setRadius(4)
- .addTo(Map_.map)
-
- Filtering.filters[layerName].spatial.feature = {
- type: 'Feature',
- properties: {},
- geometry: {
- type: 'Point',
- coordinates: [center.lng, center.lat],
- },
- }
- }
- Filtering.mapSpatialLayer.bringToFront()
- },
- // To highlight the submit button to indicate a change's been made in the form
- setSubmitButtonState: function (active) {
- if (active) {
- $('#layersTool_filtering_submit_text').text('Submit')
- $('#layersTool_filtering_submit').addClass('active')
- } else if ($('#layersTool_filtering_submit').hasClass('active')) {
- $('#layersTool_filtering_submit_text').text('Submitted')
- $('#layersTool_filtering_submit').removeClass('active')
- }
- },
- attachEvents: function (layerName) {
- // Add Value
- $('#layersTool_filtering_add_value').on('click', function () {
- Filtering.addValue(layerName)
- })
-
- // Draw
- $('#layerTool_filtering_filters_spatial_draw').on('click', function () {
- Map_.rmNotNull(Filtering.mapSpatialLayer)
- $('#map').css('cursor', 'crosshair')
- $('#layerTool_filtering_filters_spatial_draw > div').text(
- 'Placing Point'
- )
- $('#layerTool_filtering_filters_spatial').removeClass('drawn')
- $('#layerTool_filtering_filters_spatial').addClass('drawing')
- Map_.map.on('click', spatialOnClick)
- })
- function spatialOnClick(e) {
- Map_.map.off('click', spatialOnClick)
- $('#map').css('cursor', 'grab')
- $('#layerTool_filtering_filters_spatial_draw > div').text('Active')
- $('#layerTool_filtering_filters_spatial').removeClass('drawing')
- $('#layerTool_filtering_filters_spatial').addClass('drawn')
-
- Filtering.filters[layerName].spatial.center = {
- lng: e.latlng.lng,
- lat: e.latlng.lat,
- }
- Filtering.drawSpatialLayer(
- layerName,
- Filtering.filters[layerName].spatial.center,
- Filtering.filters[layerName].spatial.radius
- )
- }
- // Draw - Radius
- $('#layerTool_filtering_filters_spatial_radius').on(
- 'input',
- function (e) {
- Filtering.filters[layerName].spatial.radius = parseFloat(
- $(this).val()
- )
- Filtering.drawSpatialLayer(
- layerName,
- Filtering.filters[layerName].spatial.center,
- Filtering.filters[layerName].spatial.radius
- )
- }
- )
- // Draw - Clear
- $('#layerTool_filtering_filters_spatial_clear').on(
- 'click',
- function () {
- Filtering.filters[layerName].spatial.center = null
- Map_.map.off('click', spatialOnClick)
- $('#map').css('cursor', 'grab')
- $('#layerTool_filtering_filters_spatial_draw > div').text(
- 'Place Point'
- )
- $('#layerTool_filtering_filters_spatial').removeClass('drawn')
- $('#layerTool_filtering_filters_spatial').removeClass('drawing')
-
- Filtering.drawSpatialLayer(
- layerName,
- Filtering.filters[layerName].spatial.center,
- Filtering.filters[layerName].spatial.radius
- )
- }
- )
-
- // Submit
- $(`#layersTool_filtering_submit`).on('click', async () => {
- Filtering.setSubmitButtonState(true)
- $(`#layersTool_filtering_submit_loading`).addClass('active')
- if (Filtering.current.type === 'vector') {
- LocalFilterer.filter(layerName, Filtering.filters[layerName])
- } else if (Filtering.current.type === 'query') {
- await ESFilterer.filter(
- layerName,
- Filtering.filters[layerName],
- Filtering.getConfig()
- )
- }
-
- $(`#layersTool_filtering_submit_loading`).removeClass('active')
- Filtering.setSubmitButtonState(false)
-
- if (Filtering.mapSpatialLayer)
- Filtering.mapSpatialLayer.bringToFront()
- })
-
- // Clear
- $(`#layersTool_filtering_clear`).on('click', async () => {
- // Clear Spatial Filter
- $('#layerTool_filtering_filters_spatial_clear').click()
- $(`#layersTool_filtering_submit_loading`).addClass('active')
-
- // Clear value filter elements
- Filtering.filters[layerName].values = Filtering.filters[
- layerName
- ].values.filter((v) => {
- if (v)
- $(
- `#layersTool_filtering_value_${F_.getSafeName(
- layerName
- )}_${v.id}`
- ).remove()
- return false
- })
-
- // Refilter to show all
- if (Filtering.current.type === 'vector') {
- LocalFilterer.filter(layerName, Filtering.filters[layerName])
- } else if (Filtering.current.type === 'query') {
- await ESFilterer.filter(
- layerName,
- Filtering.filters[layerName],
- Filtering.getConfig()
- )
- }
-
- // Reset count
- $('#layersTool_filtering_count').text('')
-
- Filtering.setSubmitButtonState(false)
-
- $(`#layersTool_filtering_submit_loading`).removeClass('active')
-
- if (Filtering.mapSpatialLayer)
- Filtering.mapSpatialLayer.bringToFront()
- })
- },
- attachValueEvents: function (id, layerName, options) {
- options = options || {}
-
- let elmId
-
- // Expand input boxes on focus
- // Contract input boxes on blur
- elmId = `#layersTool_filtering_value_key_input_${F_.getSafeName(
- layerName
- )}_${id}`
- $(elmId).on('focus', function () {
- $(this).parent().css('flex', '4 1')
- })
- $(elmId).on('blur', function () {
- $(this).parent().css('flex', '1 1')
- })
- elmId = `#layersTool_filtering_value_value_input_${F_.getSafeName(
- layerName
- )}_${id}`
- $(elmId).on('focus', function () {
- $(this).parent().css('flex', '4 1')
- })
- $(elmId).on('blur', function () {
- $(this).parent().css('flex', '1 1')
- })
- // Clear
- elmId = `#layersTool_filtering_value_clear_${F_.getSafeName(
- layerName
- )}_${id}`
-
- $(elmId).on('click', () => {
- // Clear value filter element
- for (
- let i = 0;
- i < Filtering.filters[layerName].values.length;
- i++
- ) {
- const vId = Filtering.filters[layerName].values[i]?.id
- if (vId != null && vId === id) {
- $(
- `#layersTool_filtering_value_${F_.getSafeName(
- layerName
- )}_${vId}`
- ).remove()
- Filtering.filters[layerName].values[i] = null
- }
- }
- Filtering.setSubmitButtonState(true)
- })
-
- // Property Autocomplete
- elmId = `#layersTool_filtering_value_key_input_${F_.getSafeName(
- layerName
- )}_${id}`
-
- let arrayToSearch = Object.keys(Filtering.filters[layerName].aggs)
- arrayToSearch = arrayToSearch.sort((a, b) => b.localeCompare(a))
-
- $(elmId).autocomplete({
- lookup: arrayToSearch,
- lookupLimit: 100,
- minChars: 0,
- transformResult: function (response, originalQuery) {
- let resultSuggestions = []
- $.map(response, function (jsonItem) {
- if (typeof jsonItem != 'string') {
- $.map(jsonItem, function (suggestionItem) {
- resultSuggestions.push(suggestionItem)
- })
- }
- })
- resultSuggestions.sort(function (a, b) {
- const aStart = String(a.value).match(
- new RegExp(originalQuery, 'i')
- ) || { index: -1 },
- bStart = String(b.value).match(
- new RegExp(originalQuery, 'i')
- ) || { index: -1 }
- if (aStart.index != bStart.index)
- return aStart.index - bStart.index
- else return a > b ? 1 : -1
- })
- response.suggestions = resultSuggestions
- return response
- },
- onSelect: function (event) {
- const property = Filtering.filters[layerName].aggs[event.value]
- Filtering.filters[layerName].values[id].type = property.type
- Filtering.filters[layerName].values[id].key = event.value
- Filtering.updateValuesAutoComplete(id, layerName)
- Filtering.setSubmitButtonState(true)
- $(this).css('border', 'none')
- $(this).css(
- 'border-left',
- `6px solid ${F_.stringToColor(event.value)}`
- )
- },
- })
-
- $(elmId).on('blur', function (event) {
- const property =
- Filtering.filters[layerName].aggs[event.value || $(this).val()]
- if (property) {
- if (
- Filtering.filters[layerName].values[id] &&
- Filtering.filters[layerName].values[id].key !== event.value
- ) {
- Filtering.filters[layerName].values[id].key = event.value
- Filtering.filters[layerName].values[id].type = property.type
- Filtering.updateValuesAutoComplete(id, layerName)
- Filtering.setSubmitButtonState(true)
- }
- $(this).css('border', 'none')
- $(this).css(
- 'border-left',
- `6px solid ${F_.stringToColor($(this).val())}`
- )
- } else $(this).css('border', '1px solid red')
- })
-
- // Operator Dropdown
- elmId = `#layersTool_filtering_value_operator_${F_.getSafeName(
- layerName
- )}_${id}`
-
- const ops = ['=', ',', '<', '>']
- const opId = Math.max(ops.indexOf(options.op), 0)
- $(elmId).html(
- Dropy.construct(
- [
- ``,
- `in
`,
- ``,
- ``,
- ],
- 'op',
- opId,
- { openUp: true, hideChevron: true }
- )
- )
- Dropy.init($(elmId), function (idx) {
- Filtering.filters[layerName].values[id].op = ops[idx]
- Filtering.setSubmitButtonState(true)
- })
-
- // Value AutoComplete
- Filtering.updateValuesAutoComplete(id, layerName)
- },
- updateValuesAutoComplete: function (id, layerName) {
- let elmId = `#layersTool_filtering_value_value_input_${F_.getSafeName(
- layerName
- )}_${id}`
- let arrayToSearch = []
- if (
- Filtering.filters[layerName].values[id].key &&
- Filtering.filters[layerName].aggs[
- Filtering.filters[layerName].values[id].key
- ]
- )
- arrayToSearch = Object.keys(
- Filtering.filters[layerName].aggs[
- Filtering.filters[layerName].values[id].key
- ].aggs || {}
- )
- $(elmId).autocomplete({
- lookup: arrayToSearch,
- lookupLimit: 150,
- minChars: 0,
- transformResult: function (response, originalQuery) {
- let resultSuggestions = []
- $.map(response, function (jsonItem) {
- if (typeof jsonItem != 'string') {
- $.map(jsonItem, function (suggestionItem) {
- resultSuggestions.push(suggestionItem)
- })
- }
- })
- resultSuggestions.sort(function (a, b) {
- const aStart = String(a.value).match(
- new RegExp(originalQuery, 'i')
- ) || { index: -1 },
- bStart = String(b.value).match(
- new RegExp(originalQuery, 'i')
- ) || { index: -1 }
- if (aStart.index != bStart.index)
- return aStart.index - bStart.index
- else return a > b ? 1 : -1
- })
- response.suggestions = resultSuggestions
- return response
- },
- onSelect: function (event) {
- Filtering.filters[layerName].values[id].value = event.value
- Filtering.setSubmitButtonState(true)
- },
- })
- $(elmId).on('keyup', function (e) {
- Filtering.filters[layerName].values[id].value = $(this).val()
- Filtering.setSubmitButtonState(true)
- })
-
- $('.autocomplete-suggestions').css({
- 'max-height': '300px',
- 'border-top': 'none',
- })
-
- // Change type indicator icons too
- const numberElmId = `#layersTool_filtering_value_value_type_number_${F_.getSafeName(
- layerName
- )}_${id}`
- const stringElmId = `#layersTool_filtering_value_value_type_string_${F_.getSafeName(
- layerName
- )}_${id}`
- switch (Filtering.filters[layerName].values[id].type) {
- case 'number':
- $(numberElmId).css('display', 'inherit')
- $(stringElmId).css('display', 'none')
- break
- case 'string':
- $(stringElmId).css('display', 'inherit')
- $(numberElmId).css('display', 'none')
- break
- default:
- $(numberElmId).css('display', 'none')
- $(stringElmId).css('display', 'none')
- break
- }
- },
- getConfig: function () {
- if (
- Filtering.current.layerObj.type === 'query' &&
- Filtering.current.layerObj.query
- ) {
- return {
- endpoint: Filtering.current.layerObj.query.endpoint,
- type: Filtering.current.layerObj.query.type || 'elasticsearch',
- ...(Filtering.current.layerObj.variables
- ? Filtering.current.layerObj.variables.query || {}
- : {}),
- }
- }
- return {}
- },
-}
-
-export default Filtering
+// Part of the LayersTool that deals with filtering
+
+import $ from 'jquery'
+import F_ from '../../Formulae_/Formulae_'
+import L_ from '../../Layers_/Layers_'
+import Map_ from '../../Map_/Map_'
+
+import LocalFilterer from '../../../Ancillary/LocalFilterer'
+import ESFilterer from './ESFilterer'
+
+import Help from '../../../Ancillary/Help'
+import Dropy from '../../../../external/Dropy/dropy'
+import { circle } from '@turf/turf'
+
+import './Filtering.css'
+
+const helpKey = 'LayersTool-Filtering'
+
+const Filtering = {
+ filters: {},
+ current: {},
+ mapSpatialLayer: null,
+ make: async function (container, layerName) {
+ const layerObj = L_.layers.data[layerName]
+
+ if (layerObj == null) return
+
+ Filtering.filters[layerName] = Filtering.filters[layerName] || {
+ spatial: {
+ center: null,
+ radius: 0,
+ },
+ values: [],
+ geojson: null,
+ }
+ Filtering.current = {
+ layerName: layerName,
+ layerObj: layerObj,
+ type: layerObj.type,
+ }
+
+ if (Filtering.current.type === 'vector') {
+ try {
+ Filtering.filters[layerName].geojson =
+ Filtering.filters[layerName].geojson ||
+ L_.layers.layer[layerName].toGeoJSON(L_.GEOJSON_PRECISION)
+ } catch (err) {
+ console.warn(
+ `Filtering - Cannot find GeoJSON to filter on for layer: ${layerName}`
+ )
+ return
+ }
+ Filtering.filters[layerName].aggs = LocalFilterer.getAggregations(
+ Filtering.filters[layerName].geojson
+ )
+ } else if (Filtering.current.type === 'query') {
+ Filtering.filters[layerName].aggs =
+ await ESFilterer.getAggregations(
+ layerName,
+ Filtering.getConfig()
+ )
+ }
+ const spatialActive =
+ Filtering.filters[layerName].spatial?.center != null
+
+ // prettier-ignore
+ const markup = [
+ "",
+ ].join('\n')
+
+ container.append(markup)
+
+ Filtering.filters[layerName].values.forEach((v) => {
+ if (v) Filtering.addValue(layerName, v)
+ })
+
+ Filtering.attachEvents(layerName)
+
+ Filtering.drawSpatialLayer(
+ layerName,
+ Filtering.filters[layerName].spatial.center,
+ Filtering.filters[layerName].spatial.radius
+ )
+
+ // Start with one empty row added
+ if (
+ $('#layerTool_filtering_filters_list .layersTool_filtering_value')
+ .length === 0
+ )
+ Filtering.addValue(layerName)
+
+ Help.finalize(helpKey)
+ },
+ destroy: function () {
+ // Clear Spatial Filter
+ Map_.rmNotNull(Filtering.mapSpatialLayer)
+
+ $('#layersTool_filtering').remove()
+ },
+ addValue: function (layerName, value) {
+ let id, key, op, val
+ if (value) {
+ id = value.id
+ key = value.key != null ? ` value='${value.key}'` : ''
+ op = value.op
+ val = value.value != null ? ` value='${value.value}'` : ''
+ } else id = Filtering.filters[layerName].values.length
+
+ // prettier-ignore
+ const valueMarkup = [
+ `",
+ ].join('\n')
+
+ $('#layerTool_filtering_filters_list').append(valueMarkup)
+
+ if (value == null) {
+ Filtering.filters[layerName].values.push({
+ id: id,
+ type: null,
+ key: null,
+ op: '=',
+ value: null,
+ })
+ }
+
+ Filtering.attachValueEvents(id, layerName, { op: op })
+
+ // Show footer iff value rows exist
+ $('#layersTool_filtering_footer').css(
+ 'display',
+ Filtering.filters[layerName].values.length === 0 ? 'none' : 'flex'
+ )
+ },
+ drawSpatialLayer: function (layerName, center, radius) {
+ Map_.rmNotNull(Filtering.mapSpatialLayer)
+
+ Filtering.setSubmitButtonState(true)
+ if (center == null) return
+
+ const style = {
+ fillOpacity: 0.1,
+ fillColor: 'white',
+ color: 'lime',
+ weight: 2,
+ opacity: 1,
+ className: 'noPointerEventsImportant',
+ }
+
+ if (radius > 0) {
+ // Buffered Circle
+ const geojson = F_.getBaseGeoJSON()
+ geojson.features.push(
+ circle(
+ [center.lng, center.lat],
+ radius * 0.001 * F_.getEarthToPlanetRatio()
+ )
+ )
+
+ Filtering.mapSpatialLayer = L.geoJSON(geojson, {
+ style: style,
+ }).addTo(Map_.map)
+ Filtering.filters[layerName].spatial.feature = geojson.features[0]
+ } else {
+ // Circle marker
+ Filtering.mapSpatialLayer = new L.circleMarker(
+ [center.lat, center.lng],
+ style
+ )
+ .setRadius(4)
+ .addTo(Map_.map)
+
+ Filtering.filters[layerName].spatial.feature = {
+ type: 'Feature',
+ properties: {},
+ geometry: {
+ type: 'Point',
+ coordinates: [center.lng, center.lat],
+ },
+ }
+ }
+ Filtering.mapSpatialLayer.bringToFront()
+ },
+ // To highlight the submit button to indicate a change's been made in the form
+ setSubmitButtonState: function (active) {
+ if (active) {
+ $('#layersTool_filtering_submit_text').text('Submit')
+ $('#layersTool_filtering_submit').addClass('active')
+ } else if ($('#layersTool_filtering_submit').hasClass('active')) {
+ $('#layersTool_filtering_submit_text').text('Submitted')
+ $('#layersTool_filtering_submit').removeClass('active')
+ }
+ },
+ attachEvents: function (layerName) {
+ // Add Value
+ $('#layersTool_filtering_add_value').on('click', function () {
+ Filtering.addValue(layerName)
+ })
+
+ // Draw
+ $('#layerTool_filtering_filters_spatial_draw').on('click', function () {
+ Map_.rmNotNull(Filtering.mapSpatialLayer)
+ $('#map').css('cursor', 'crosshair')
+ $('#layerTool_filtering_filters_spatial_draw > div').text(
+ 'Placing Point'
+ )
+ $('#layerTool_filtering_filters_spatial').removeClass('drawn')
+ $('#layerTool_filtering_filters_spatial').addClass('drawing')
+ Map_.map.on('click', spatialOnClick)
+ })
+ function spatialOnClick(e) {
+ Map_.map.off('click', spatialOnClick)
+ $('#map').css('cursor', 'grab')
+ $('#layerTool_filtering_filters_spatial_draw > div').text('Active')
+ $('#layerTool_filtering_filters_spatial').removeClass('drawing')
+ $('#layerTool_filtering_filters_spatial').addClass('drawn')
+
+ Filtering.filters[layerName].spatial.center = {
+ lng: e.latlng.lng,
+ lat: e.latlng.lat,
+ }
+ Filtering.drawSpatialLayer(
+ layerName,
+ Filtering.filters[layerName].spatial.center,
+ Filtering.filters[layerName].spatial.radius
+ )
+ }
+ // Draw - Radius
+ $('#layerTool_filtering_filters_spatial_radius').on(
+ 'input',
+ function (e) {
+ Filtering.filters[layerName].spatial.radius = parseFloat(
+ $(this).val()
+ )
+ Filtering.drawSpatialLayer(
+ layerName,
+ Filtering.filters[layerName].spatial.center,
+ Filtering.filters[layerName].spatial.radius
+ )
+ }
+ )
+ // Draw - Clear
+ $('#layerTool_filtering_filters_spatial_clear').on(
+ 'click',
+ function () {
+ Filtering.filters[layerName].spatial.center = null
+ Map_.map.off('click', spatialOnClick)
+ $('#map').css('cursor', 'grab')
+ $('#layerTool_filtering_filters_spatial_draw > div').text(
+ 'Place Point'
+ )
+ $('#layerTool_filtering_filters_spatial').removeClass('drawn')
+ $('#layerTool_filtering_filters_spatial').removeClass('drawing')
+
+ Filtering.drawSpatialLayer(
+ layerName,
+ Filtering.filters[layerName].spatial.center,
+ Filtering.filters[layerName].spatial.radius
+ )
+ }
+ )
+
+ // Submit
+ $(`#layersTool_filtering_submit`).on('click', async () => {
+ Filtering.setSubmitButtonState(true)
+ $(`#layersTool_filtering_submit_loading`).addClass('active')
+ if (Filtering.current.type === 'vector') {
+ LocalFilterer.filter(layerName, Filtering.filters[layerName])
+ } else if (Filtering.current.type === 'query') {
+ await ESFilterer.filter(
+ layerName,
+ Filtering.filters[layerName],
+ Filtering.getConfig()
+ )
+ }
+
+ $(`#layersTool_filtering_submit_loading`).removeClass('active')
+ Filtering.setSubmitButtonState(false)
+
+ if (Filtering.mapSpatialLayer)
+ Filtering.mapSpatialLayer.bringToFront()
+ })
+
+ // Clear
+ $(`#layersTool_filtering_clear`).on('click', async () => {
+ // Clear Spatial Filter
+ $('#layerTool_filtering_filters_spatial_clear').click()
+ $(`#layersTool_filtering_submit_loading`).addClass('active')
+
+ // Clear value filter elements
+ Filtering.filters[layerName].values = Filtering.filters[
+ layerName
+ ].values.filter((v) => {
+ if (v)
+ $(
+ `#layersTool_filtering_value_${F_.getSafeName(
+ layerName
+ )}_${v.id}`
+ ).remove()
+ return false
+ })
+
+ // Refilter to show all
+ if (Filtering.current.type === 'vector') {
+ LocalFilterer.filter(layerName, Filtering.filters[layerName])
+ } else if (Filtering.current.type === 'query') {
+ await ESFilterer.filter(
+ layerName,
+ Filtering.filters[layerName],
+ Filtering.getConfig()
+ )
+ }
+
+ // Reset count
+ $('#layersTool_filtering_count').text('')
+
+ Filtering.setSubmitButtonState(false)
+
+ $(`#layersTool_filtering_submit_loading`).removeClass('active')
+
+ if (Filtering.mapSpatialLayer)
+ Filtering.mapSpatialLayer.bringToFront()
+ })
+ },
+ attachValueEvents: function (id, layerName, options) {
+ options = options || {}
+
+ let elmId
+
+ // Expand input boxes on focus
+ // Contract input boxes on blur
+ elmId = `#layersTool_filtering_value_key_input_${F_.getSafeName(
+ layerName
+ )}_${id}`
+ $(elmId).on('focus', function () {
+ $(this).parent().css('flex', '4 1')
+ })
+ $(elmId).on('blur', function () {
+ $(this).parent().css('flex', '1 1')
+ })
+ elmId = `#layersTool_filtering_value_value_input_${F_.getSafeName(
+ layerName
+ )}_${id}`
+ $(elmId).on('focus', function () {
+ $(this).parent().css('flex', '4 1')
+ })
+ $(elmId).on('blur', function () {
+ $(this).parent().css('flex', '1 1')
+ })
+ // Clear
+ elmId = `#layersTool_filtering_value_clear_${F_.getSafeName(
+ layerName
+ )}_${id}`
+
+ $(elmId).on('click', () => {
+ // Clear value filter element
+ for (
+ let i = 0;
+ i < Filtering.filters[layerName].values.length;
+ i++
+ ) {
+ const vId = Filtering.filters[layerName].values[i]?.id
+ if (vId != null && vId === id) {
+ $(
+ `#layersTool_filtering_value_${F_.getSafeName(
+ layerName
+ )}_${vId}`
+ ).remove()
+ Filtering.filters[layerName].values[i] = null
+ }
+ }
+ Filtering.setSubmitButtonState(true)
+ })
+
+ // Property Autocomplete
+ elmId = `#layersTool_filtering_value_key_input_${F_.getSafeName(
+ layerName
+ )}_${id}`
+
+ let arrayToSearch = Object.keys(Filtering.filters[layerName].aggs)
+ arrayToSearch = arrayToSearch.sort((a, b) => b.localeCompare(a))
+
+ $(elmId).autocomplete({
+ lookup: arrayToSearch,
+ lookupLimit: 100,
+ minChars: 0,
+ transformResult: function (response, originalQuery) {
+ let resultSuggestions = []
+ $.map(response, function (jsonItem) {
+ if (typeof jsonItem != 'string') {
+ $.map(jsonItem, function (suggestionItem) {
+ resultSuggestions.push(suggestionItem)
+ })
+ }
+ })
+ resultSuggestions.sort(function (a, b) {
+ const aStart = String(a.value).match(
+ new RegExp(originalQuery, 'i')
+ ) || { index: -1 },
+ bStart = String(b.value).match(
+ new RegExp(originalQuery, 'i')
+ ) || { index: -1 }
+ if (aStart.index != bStart.index)
+ return aStart.index - bStart.index
+ else return a > b ? 1 : -1
+ })
+ response.suggestions = resultSuggestions
+ return response
+ },
+ onSelect: function (event) {
+ const property = Filtering.filters[layerName].aggs[event.value]
+ Filtering.filters[layerName].values[id].type = property.type
+ Filtering.filters[layerName].values[id].key = event.value
+ Filtering.updateValuesAutoComplete(id, layerName)
+ Filtering.setSubmitButtonState(true)
+ $(this).css('border', 'none')
+ $(this).css(
+ 'border-left',
+ `6px solid ${F_.stringToColor(event.value)}`
+ )
+ },
+ })
+
+ $(elmId).on('blur', function (event) {
+ const property =
+ Filtering.filters[layerName].aggs[event.value || $(this).val()]
+ if (property) {
+ if (
+ Filtering.filters[layerName].values[id] &&
+ Filtering.filters[layerName].values[id].key !== event.value
+ ) {
+ Filtering.filters[layerName].values[id].key = event.value
+ Filtering.filters[layerName].values[id].type = property.type
+ Filtering.updateValuesAutoComplete(id, layerName)
+ Filtering.setSubmitButtonState(true)
+ }
+ $(this).css('border', 'none')
+ $(this).css(
+ 'border-left',
+ `6px solid ${F_.stringToColor($(this).val())}`
+ )
+ } else $(this).css('border', '1px solid red')
+ })
+
+ // Operator Dropdown
+ elmId = `#layersTool_filtering_value_operator_${F_.getSafeName(
+ layerName
+ )}_${id}`
+
+ const ops = ['=', ',', '<', '>']
+ const opId = Math.max(ops.indexOf(options.op), 0)
+ $(elmId).html(
+ Dropy.construct(
+ [
+ ``,
+ `in
`,
+ ``,
+ ``,
+ ],
+ 'op',
+ opId,
+ { openUp: true, hideChevron: true }
+ )
+ )
+ Dropy.init($(elmId), function (idx) {
+ Filtering.filters[layerName].values[id].op = ops[idx]
+ Filtering.setSubmitButtonState(true)
+ })
+
+ // Value AutoComplete
+ Filtering.updateValuesAutoComplete(id, layerName)
+ },
+ updateValuesAutoComplete: function (id, layerName) {
+ let elmId = `#layersTool_filtering_value_value_input_${F_.getSafeName(
+ layerName
+ )}_${id}`
+ let arrayToSearch = []
+ if (
+ Filtering.filters[layerName].values[id].key &&
+ Filtering.filters[layerName].aggs[
+ Filtering.filters[layerName].values[id].key
+ ]
+ )
+ arrayToSearch = Object.keys(
+ Filtering.filters[layerName].aggs[
+ Filtering.filters[layerName].values[id].key
+ ].aggs || {}
+ )
+ $(elmId).autocomplete({
+ lookup: arrayToSearch,
+ lookupLimit: 150,
+ minChars: 0,
+ transformResult: function (response, originalQuery) {
+ let resultSuggestions = []
+ $.map(response, function (jsonItem) {
+ if (typeof jsonItem != 'string') {
+ $.map(jsonItem, function (suggestionItem) {
+ resultSuggestions.push(suggestionItem)
+ })
+ }
+ })
+ resultSuggestions.sort(function (a, b) {
+ const aStart = String(a.value).match(
+ new RegExp(originalQuery, 'i')
+ ) || { index: -1 },
+ bStart = String(b.value).match(
+ new RegExp(originalQuery, 'i')
+ ) || { index: -1 }
+ if (aStart.index != bStart.index)
+ return aStart.index - bStart.index
+ else return a > b ? 1 : -1
+ })
+ response.suggestions = resultSuggestions
+ return response
+ },
+ onSelect: function (event) {
+ Filtering.filters[layerName].values[id].value = event.value
+ Filtering.setSubmitButtonState(true)
+ },
+ })
+ $(elmId).on('keyup', function (e) {
+ Filtering.filters[layerName].values[id].value = $(this).val()
+ Filtering.setSubmitButtonState(true)
+ })
+
+ $('.autocomplete-suggestions').css({
+ 'max-height': '300px',
+ 'border-top': 'none',
+ })
+
+ // Change type indicator icons too
+ const numberElmId = `#layersTool_filtering_value_value_type_number_${F_.getSafeName(
+ layerName
+ )}_${id}`
+ const stringElmId = `#layersTool_filtering_value_value_type_string_${F_.getSafeName(
+ layerName
+ )}_${id}`
+ switch (Filtering.filters[layerName].values[id].type) {
+ case 'number':
+ $(numberElmId).css('display', 'inherit')
+ $(stringElmId).css('display', 'none')
+ break
+ case 'string':
+ $(stringElmId).css('display', 'inherit')
+ $(numberElmId).css('display', 'none')
+ break
+ default:
+ $(numberElmId).css('display', 'none')
+ $(stringElmId).css('display', 'none')
+ break
+ }
+ },
+ getConfig: function () {
+ if (
+ Filtering.current.layerObj.type === 'query' &&
+ Filtering.current.layerObj.query
+ ) {
+ return {
+ endpoint: Filtering.current.layerObj.query.endpoint,
+ type: Filtering.current.layerObj.query.type || 'elasticsearch',
+ ...(Filtering.current.layerObj.variables
+ ? Filtering.current.layerObj.variables.query || {}
+ : {}),
+ }
+ }
+ return {}
+ },
+ // Let other places of the code trigger filters as needed
+ triggerFilter: function (layerName) {
+ if (Filtering.filters[layerName]) {
+ if (L_.layers.data[layerName].type === 'vector')
+ if (Filtering.filters[layerName]?.values?.[0]?.type != null) {
+ LocalFilterer.filter(
+ layerName,
+ Filtering.filters[layerName]
+ )
+ }
+ }
+ },
+ // Useful for dynamicExtent vector layers so that the geojson and aggs match the visible features
+ updateGeoJSON: async function (layerName) {
+ if (Filtering.filters[layerName]) {
+ if (L_.layers.data[layerName].type === 'vector') {
+ try {
+ Filtering.filters[layerName].geojson = L_.layers.layer[
+ layerName
+ ].toGeoJSON(L_.GEOJSON_PRECISION)
+ } catch (err) {
+ console.warn(
+ `Filtering - Cannot find GeoJSON to filter on for layer: ${layerName}`
+ )
+ return
+ }
+ Filtering.filters[layerName].aggs =
+ LocalFilterer.getAggregations(
+ Filtering.filters[layerName].geojson
+ )
+ } else if (L_.layers.data[layerName].type === 'query')
+ Filtering.filters[layerName].aggs =
+ await ESFilterer.getAggregations(
+ layerName,
+ Filtering.getConfig()
+ )
+
+ if (Filtering.filters[layerName]?.values) {
+ Filtering.filters[layerName]?.values.forEach((v, idx) => {
+ // Value AutoComplete
+ Filtering.updateValuesAutoComplete(idx, layerName)
+ })
+ }
+ }
+ },
+}
+
+export default Filtering
diff --git a/src/essence/Basics/Layers_/Layers_.js b/src/essence/Basics/Layers_/Layers_.js
index c6c9b88c..6f6d6029 100644
--- a/src/essence/Basics/Layers_/Layers_.js
+++ b/src/essence/Basics/Layers_/Layers_.js
@@ -914,7 +914,7 @@ const L_ = {
L_._refreshAnnotationEvents()
},
- addGeoJSONData: function (layer, geojson, keepLastN) {
+ addGeoJSONData: function (layer, geojson, keepLastN, stopLoops) {
if (layer._sourceGeoJSON) {
if (layer._sourceGeoJSON.features)
if (geojson.features)
@@ -953,7 +953,10 @@ const L_ = {
L_.Map_.makeLayer(
L_.layers.data[layer._layerName],
true,
- layer._sourceGeoJSON
+ layer._sourceGeoJSON,
+ null,
+ null,
+ stopLoops
)
if (initialOn) {
@@ -2695,7 +2698,7 @@ const L_ = {
}
return true
},
- updateVectorLayer: function (layerName, inputData, keepLastN) {
+ updateVectorLayer: function (layerName, inputData, keepLastN, stopLoops) {
layerName = L_.asLayerUUID(layerName)
if (layerName in L_.layers.layer) {
@@ -2710,7 +2713,7 @@ const L_ = {
const updateLayer = L_.layers.layer[layerName]
try {
- L_.addGeoJSONData(updateLayer, inputData, keepLastN)
+ L_.addGeoJSONData(updateLayer, inputData, keepLastN, stopLoops)
} catch (e) {
console.log(e)
console.warn(
diff --git a/src/essence/Basics/Map_/Map_.js b/src/essence/Basics/Map_/Map_.js
index 0478d261..808c1e22 100644
--- a/src/essence/Basics/Map_/Map_.js
+++ b/src/essence/Basics/Map_/Map_.js
@@ -7,6 +7,7 @@ import {
constructVectorLayer,
constructSublayers,
} from '../Layers_/LayerConstructors'
+import Filtering from '../Layers_/Filtering/Filtering'
import Viewer_ from '../Viewer_/Viewer_'
import Globe_ from '../Globe_/Globe_'
import ToolController_ from '../ToolController_/ToolController_'
@@ -530,7 +531,14 @@ function makeLayers(layersObj) {
}
}
//Takes the layer object and makes it a map layer
-async function makeLayer(layerObj, evenIfOff, forceGeoJSON, id, forceMake) {
+async function makeLayer(
+ layerObj,
+ evenIfOff,
+ forceGeoJSON,
+ id,
+ forceMake,
+ stopLoops
+) {
return new Promise(async (resolve, reject) => {
const layerName = L_.asLayerUUID(layerObj.name)
if (forceMake !== true && L_._layersBeingMade[layerName] === true) {
@@ -579,6 +587,11 @@ async function makeLayer(layerObj, evenIfOff, forceGeoJSON, id, forceMake) {
// release hold on layer
L_._layersBeingMade[layerName] = false
+ if (stopLoops !== true && layerObj.type === 'vector') {
+ Filtering.updateGeoJSON(layerObj.name)
+ Filtering.triggerFilter(layerObj.name)
+ }
+
resolve(true)
})
}
@@ -871,6 +884,7 @@ async function makeVectorLayer(
data.features
)
L_._layersLoaded[L_._layersOrdered.indexOf(layerObj.name)] = true
+
allLayersLoaded()
resolve()
}
diff --git a/src/essence/Tools/Layers/LayersTool.js b/src/essence/Tools/Layers/LayersTool.js
index 1bbd8ccd..5fe08e41 100644
--- a/src/essence/Tools/Layers/LayersTool.js
+++ b/src/essence/Tools/Layers/LayersTool.js
@@ -7,7 +7,7 @@ import Map_ from '../../Basics/Map_/Map_'
import DataShaders from '../../Ancillary/DataShaders'
import LayerInfoModal from './LayerInfoModal/LayerInfoModal'
-import Filtering from './Filtering/Filtering'
+import Filtering from '../../Basics/Layers_/Filtering/Filtering'
import Help from '../../Ancillary/Help'
import CursorInfo from '../../Ancillary/CursorInfo'