From 4d7c3650b55c3cb1fc667999899a834270f6f5ea Mon Sep 17 00:00:00 2001 From: MaysWind Date: Sun, 8 Mar 2026 20:55:35 +0800 Subject: [PATCH] support filtering by geographic longitude and latitude in insights explorer queries --- src/core/explorer.ts | 10 +- src/locales/de.json | 4 + src/locales/en.json | 4 + src/locales/es.json | 4 + src/locales/fr.json | 4 + src/locales/it.json | 4 + src/locales/ja.json | 4 + src/locales/kn.json | 4 + src/locales/ko.json | 4 + src/locales/nl.json | 4 + src/locales/pt_BR.json | 4 + src/locales/ru.json | 4 + src/locales/sl.json | 4 + src/locales/ta.json | 4 + src/locales/th.json | 4 + src/locales/tr.json | 4 + src/locales/uk.json | 4 + src/locales/vi.json | 4 + src/locales/zh_Hans.json | 4 + src/locales/zh_Hant.json | 4 + src/models/explorer.ts | 120 ++++++++++++++++-- .../insights/tabs/ExplorerQueryTab.vue | 58 ++++++++- 22 files changed, 244 insertions(+), 20 deletions(-) diff --git a/src/core/explorer.ts b/src/core/explorer.ts index 30849d5b..841533db 100644 --- a/src/core/explorer.ts +++ b/src/core/explorer.ts @@ -81,7 +81,11 @@ export enum TransactionExplorerConditionOperatorType { StartsWith = 'startsWith', NotStartsWith = 'notStartsWith', EndsWith = 'endsWith', - NotEndsWith = 'notEndsWith' + NotEndsWith = 'notEndsWith', + LatitudeBetween = 'latitudeBetween', + LatitudeNotBetween = 'latitudeNotBetween', + LongitudeBetween = 'longitudeBetween', + LongitudeNotBetween = 'longitudeNotBetween' } export class TransactionExplorerConditionOperator implements NameValue { @@ -107,6 +111,10 @@ export class TransactionExplorerConditionOperator implements NameValue { public static readonly NotStartsWith = new TransactionExplorerConditionOperator('Not starts with', TransactionExplorerConditionOperatorType.NotStartsWith); public static readonly EndsWith = new TransactionExplorerConditionOperator('Ends with', TransactionExplorerConditionOperatorType.EndsWith); public static readonly NotEndsWith = new TransactionExplorerConditionOperator('Not ends with', TransactionExplorerConditionOperatorType.NotEndsWith); + public static readonly LatitudeBetween = new TransactionExplorerConditionOperator('Latitude between', TransactionExplorerConditionOperatorType.LatitudeBetween); + public static readonly LatitudeNotBetween = new TransactionExplorerConditionOperator('Latitude not between', TransactionExplorerConditionOperatorType.LatitudeNotBetween); + public static readonly LongitudeBetween = new TransactionExplorerConditionOperator('Longitude between', TransactionExplorerConditionOperatorType.LongitudeBetween); + public static readonly LongitudeNotBetween = new TransactionExplorerConditionOperator('Longitude not between', TransactionExplorerConditionOperatorType.LongitudeNotBetween); public readonly name: string; public readonly value: TransactionExplorerConditionOperatorType; diff --git a/src/locales/de.json b/src/locales/de.json index ff52dd9a..586f423c 100644 --- a/src/locales/de.json +++ b/src/locales/de.json @@ -1567,6 +1567,10 @@ "Not starts with": "Not starts with", "Ends with": "Ends with", "Not ends with": "Not ends with", + "Latitude between": "Latitude between", + "Latitude not between": "Latitude not between", + "Longitude between": "Longitude between", + "Longitude not between": "Longitude not between", "Pie Chart": "Tortendiagramm", "Bar Chart": "Balkendiagramm", "Radar Chart": "Radar Chart", diff --git a/src/locales/en.json b/src/locales/en.json index 1627bcfd..0bc9723e 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -1567,6 +1567,10 @@ "Not starts with": "Not starts with", "Ends with": "Ends with", "Not ends with": "Not ends with", + "Latitude between": "Latitude between", + "Latitude not between": "Latitude not between", + "Longitude between": "Longitude between", + "Longitude not between": "Longitude not between", "Pie Chart": "Pie Chart", "Bar Chart": "Bar Chart", "Radar Chart": "Radar Chart", diff --git a/src/locales/es.json b/src/locales/es.json index 6d7ae818..c63befd1 100644 --- a/src/locales/es.json +++ b/src/locales/es.json @@ -1567,6 +1567,10 @@ "Not starts with": "No empieza por", "Ends with": "Termina en", "Not ends with": "No termina en", + "Latitude between": "Latitude between", + "Latitude not between": "Latitude not between", + "Longitude between": "Longitude between", + "Longitude not between": "Longitude not between", "Pie Chart": "Gráfico Circular", "Bar Chart": "Gráfico de Barras", "Radar Chart": "Gráfico de Radar", diff --git a/src/locales/fr.json b/src/locales/fr.json index 8a600fe4..cadb1e3b 100644 --- a/src/locales/fr.json +++ b/src/locales/fr.json @@ -1567,6 +1567,10 @@ "Not starts with": "Not starts with", "Ends with": "Ends with", "Not ends with": "Not ends with", + "Latitude between": "Latitude between", + "Latitude not between": "Latitude not between", + "Longitude between": "Longitude between", + "Longitude not between": "Longitude not between", "Pie Chart": "Graphique en secteurs", "Bar Chart": "Graphique en barres", "Radar Chart": "Radar Chart", diff --git a/src/locales/it.json b/src/locales/it.json index d3302a5a..9951474b 100644 --- a/src/locales/it.json +++ b/src/locales/it.json @@ -1567,6 +1567,10 @@ "Not starts with": "Not starts with", "Ends with": "Ends with", "Not ends with": "Not ends with", + "Latitude between": "Latitude between", + "Latitude not between": "Latitude not between", + "Longitude between": "Longitude between", + "Longitude not between": "Longitude not between", "Pie Chart": "Grafico a torta", "Bar Chart": "Grafico a barre", "Radar Chart": "Radar Chart", diff --git a/src/locales/ja.json b/src/locales/ja.json index f3d883f7..35e6a1fb 100644 --- a/src/locales/ja.json +++ b/src/locales/ja.json @@ -1567,6 +1567,10 @@ "Not starts with": "Not starts with", "Ends with": "Ends with", "Not ends with": "Not ends with", + "Latitude between": "Latitude between", + "Latitude not between": "Latitude not between", + "Longitude between": "Longitude between", + "Longitude not between": "Longitude not between", "Pie Chart": "円グラフ", "Bar Chart": "棒グラフ", "Radar Chart": "Radar Chart", diff --git a/src/locales/kn.json b/src/locales/kn.json index a74b3a8b..e775aec0 100644 --- a/src/locales/kn.json +++ b/src/locales/kn.json @@ -1567,6 +1567,10 @@ "Not starts with": "Not starts with", "Ends with": "Ends with", "Not ends with": "Not ends with", + "Latitude between": "Latitude between", + "Latitude not between": "Latitude not between", + "Longitude between": "Longitude between", + "Longitude not between": "Longitude not between", "Pie Chart": "ಪಾಯಿ ಚಾರ್ಟ್", "Bar Chart": "ಬಾರ್ ಚಾರ್ಟ್", "Radar Chart": "ರಡಾರ್ ಚಾರ್ಟ್", diff --git a/src/locales/ko.json b/src/locales/ko.json index b564c108..7150b746 100644 --- a/src/locales/ko.json +++ b/src/locales/ko.json @@ -1567,6 +1567,10 @@ "Not starts with": "Not starts with", "Ends with": "Ends with", "Not ends with": "Not ends with", + "Latitude between": "Latitude between", + "Latitude not between": "Latitude not between", + "Longitude between": "Longitude between", + "Longitude not between": "Longitude not between", "Pie Chart": "원형 차트", "Bar Chart": "막대 차트", "Radar Chart": "레이더 차트", diff --git a/src/locales/nl.json b/src/locales/nl.json index 7c339657..402880f8 100644 --- a/src/locales/nl.json +++ b/src/locales/nl.json @@ -1567,6 +1567,10 @@ "Not starts with": "Not starts with", "Ends with": "Ends with", "Not ends with": "Not ends with", + "Latitude between": "Latitude between", + "Latitude not between": "Latitude not between", + "Longitude between": "Longitude between", + "Longitude not between": "Longitude not between", "Pie Chart": "Cirkeldiagram", "Bar Chart": "Balkdiagram", "Radar Chart": "Radar Chart", diff --git a/src/locales/pt_BR.json b/src/locales/pt_BR.json index aaef2420..030926b8 100644 --- a/src/locales/pt_BR.json +++ b/src/locales/pt_BR.json @@ -1567,6 +1567,10 @@ "Not starts with": "Not starts with", "Ends with": "Ends with", "Not ends with": "Not ends with", + "Latitude between": "Latitude between", + "Latitude not between": "Latitude not between", + "Longitude between": "Longitude between", + "Longitude not between": "Longitude not between", "Pie Chart": "Gráfico de Pizza", "Bar Chart": "Gráfico de Barras", "Radar Chart": "Radar Chart", diff --git a/src/locales/ru.json b/src/locales/ru.json index 3a30ccbe..70c61294 100644 --- a/src/locales/ru.json +++ b/src/locales/ru.json @@ -1567,6 +1567,10 @@ "Not starts with": "Не начинается с", "Ends with": "Заканчивается с", "Not ends with": "Не заканчивается с", + "Latitude between": "Latitude between", + "Latitude not between": "Latitude not between", + "Longitude between": "Longitude between", + "Longitude not between": "Longitude not between", "Pie Chart": "Круговая диаграмма", "Bar Chart": "Гистограмма", "Radar Chart": "Лепестковая диаграмма", diff --git a/src/locales/sl.json b/src/locales/sl.json index 040fabc9..f38edb41 100644 --- a/src/locales/sl.json +++ b/src/locales/sl.json @@ -1567,6 +1567,10 @@ "Not starts with": "Se ne začne z", "Ends with": "Se konča z", "Not ends with": "Se ne konča z", + "Latitude between": "Latitude between", + "Latitude not between": "Latitude not between", + "Longitude between": "Longitude between", + "Longitude not between": "Longitude not between", "Pie Chart": "Tortni grafikon", "Bar Chart": "Vodoravni palični grafikon", "Radar Chart": "Radarski grafikon", diff --git a/src/locales/ta.json b/src/locales/ta.json index c848b29c..5f011618 100644 --- a/src/locales/ta.json +++ b/src/locales/ta.json @@ -1567,6 +1567,10 @@ "Not starts with": "தொடங்கவில்லை", "Ends with": "முடிகிறது", "Not ends with": "முடியவில்லை", + "Latitude between": "Latitude between", + "Latitude not between": "Latitude not between", + "Longitude between": "Longitude between", + "Longitude not between": "Longitude not between", "Pie Chart": "வட்ட விளக்கப்படம்", "Bar Chart": "பட்டை விளக்கப்படம்", "Radar Chart": "ரேடார் விளக்கப்படம்", diff --git a/src/locales/th.json b/src/locales/th.json index df8cf18d..03bd63d3 100644 --- a/src/locales/th.json +++ b/src/locales/th.json @@ -1567,6 +1567,10 @@ "Not starts with": "Not starts with", "Ends with": "Ends with", "Not ends with": "Not ends with", + "Latitude between": "Latitude between", + "Latitude not between": "Latitude not between", + "Longitude between": "Longitude between", + "Longitude not between": "Longitude not between", "Pie Chart": "กราฟวงกลม", "Bar Chart": "กราฟแท่ง", "Radar Chart": "Radar Chart", diff --git a/src/locales/tr.json b/src/locales/tr.json index d9014ba8..cd9ee27d 100644 --- a/src/locales/tr.json +++ b/src/locales/tr.json @@ -1567,6 +1567,10 @@ "Not starts with": "Not starts with", "Ends with": "Ends with", "Not ends with": "Not ends with", + "Latitude between": "Latitude between", + "Latitude not between": "Latitude not between", + "Longitude between": "Longitude between", + "Longitude not between": "Longitude not between", "Pie Chart": "Pasta Grafiği", "Bar Chart": "Çubuk Grafik", "Radar Chart": "Radar Grafiği", diff --git a/src/locales/uk.json b/src/locales/uk.json index a3af4ca3..04e4738c 100644 --- a/src/locales/uk.json +++ b/src/locales/uk.json @@ -1567,6 +1567,10 @@ "Not starts with": "Not starts with", "Ends with": "Ends with", "Not ends with": "Not ends with", + "Latitude between": "Latitude between", + "Latitude not between": "Latitude not between", + "Longitude between": "Longitude between", + "Longitude not between": "Longitude not between", "Pie Chart": "Кругова діаграма", "Bar Chart": "Гістограма", "Radar Chart": "Radar Chart", diff --git a/src/locales/vi.json b/src/locales/vi.json index 60681236..437444cd 100644 --- a/src/locales/vi.json +++ b/src/locales/vi.json @@ -1567,6 +1567,10 @@ "Not starts with": "Not starts with", "Ends with": "Ends with", "Not ends with": "Not ends with", + "Latitude between": "Latitude between", + "Latitude not between": "Latitude not between", + "Longitude between": "Longitude between", + "Longitude not between": "Longitude not between", "Pie Chart": "Biểu đồ tròn", "Bar Chart": "Biểu đồ cột", "Radar Chart": "Radar Chart", diff --git a/src/locales/zh_Hans.json b/src/locales/zh_Hans.json index 61a0dc6e..1d5a2d5d 100644 --- a/src/locales/zh_Hans.json +++ b/src/locales/zh_Hans.json @@ -1567,6 +1567,10 @@ "Not starts with": "开头不是", "Ends with": "结尾是", "Not ends with": "结尾不是", + "Latitude between": "纬度介于", + "Latitude not between": "纬度不介于", + "Longitude between": "经度介于", + "Longitude not between": "经度不介于", "Pie Chart": "饼图", "Bar Chart": "条形图", "Radar Chart": "雷达图", diff --git a/src/locales/zh_Hant.json b/src/locales/zh_Hant.json index 57cf32e6..e9bb68f9 100644 --- a/src/locales/zh_Hant.json +++ b/src/locales/zh_Hant.json @@ -1567,6 +1567,10 @@ "Not starts with": "開頭不是", "Ends with": "結尾是", "Not ends with": "結尾不是", + "Latitude between": "緯度介於", + "Latitude not between": "緯度不介於", + "Longitude between": "經度介於", + "Longitude not between": "經度不介於", "Pie Chart": "圓餅圖", "Bar Chart": "長條圖", "Radar Chart": "雷達圖", diff --git a/src/models/explorer.ts b/src/models/explorer.ts index 864d378c..2e3af52c 100644 --- a/src/models/explorer.ts +++ b/src/models/explorer.ts @@ -317,7 +317,7 @@ export class TransactionExplorerQuery { condition = new TransactionExplorerDestinationAmountCondition(TransactionExplorerConditionOperatorType.Between, [0, 0]); break; case TransactionExplorerConditionField.GeoLocation: - condition = new TransactionExplorerGeoLocationCondition(TransactionExplorerConditionOperatorType.IsNotEmpty, []); + condition = new TransactionExplorerGeoLocationCondition(TransactionExplorerConditionOperatorType.IsNotEmpty, [TransactionExplorerGeoLocationCondition.MIN_LATITUDE, TransactionExplorerGeoLocationCondition.MAX_LATITUDE, TransactionExplorerGeoLocationCondition.MIN_LONGITUDE, TransactionExplorerGeoLocationCondition.MAX_LONGITUDE]); break; case TransactionExplorerConditionField.TransactionTag: condition = new TransactionExplorerTransactionTagCondition(TransactionExplorerConditionOperatorType.HasAny, []); @@ -670,7 +670,7 @@ export class TransactionExplorerConditionWithRelation { break; case TransactionExplorerConditionField.GeoLocation.value: if (TransactionExplorerGeoLocationCondition.supportedOperators[conditionOperator] && Array.isArray(conditionValue)) { - condition = new TransactionExplorerGeoLocationCondition(conditionOperator as GeoLocationConditionOperator, conditionValue as string[]); + condition = new TransactionExplorerGeoLocationCondition(conditionOperator as GeoLocationConditionOperator, conditionValue as [number, number, number, number]); } break; case TransactionExplorerConditionField.TransactionTag.value: @@ -984,29 +984,47 @@ export class TransactionExplorerDestinationAmountCondition extends AbstractTrans } type GeoLocationConditionOperator = TransactionExplorerConditionOperatorType.IsEmpty | - TransactionExplorerConditionOperatorType.IsNotEmpty; + TransactionExplorerConditionOperatorType.IsNotEmpty | + TransactionExplorerConditionOperatorType.LatitudeBetween | + TransactionExplorerConditionOperatorType.LatitudeNotBetween | + TransactionExplorerConditionOperatorType.LongitudeBetween | + TransactionExplorerConditionOperatorType.LongitudeNotBetween; -export class TransactionExplorerGeoLocationCondition implements TransactionExplorerCondition { +export class TransactionExplorerGeoLocationCondition implements TransactionExplorerCondition { public static readonly supportedOperators: PartialRecord = { [TransactionExplorerConditionOperatorType.IsEmpty]: true, - [TransactionExplorerConditionOperatorType.IsNotEmpty]: true + [TransactionExplorerConditionOperatorType.IsNotEmpty]: true, + [TransactionExplorerConditionOperatorType.LatitudeBetween]: true, + [TransactionExplorerConditionOperatorType.LatitudeNotBetween]: true, + [TransactionExplorerConditionOperatorType.LongitudeBetween]: true, + [TransactionExplorerConditionOperatorType.LongitudeNotBetween]: true }; + public static readonly MIN_LATITUDE: number = -90.0; + public static readonly MAX_LATITUDE: number = 90.0; + public static readonly MIN_LONGITUDE: number = -180.0; + public static readonly MAX_LONGITUDE: number = 180.0; public readonly field = TransactionExplorerConditionFieldType.GeoLocation; public readonly operator: GeoLocationConditionOperator = TransactionExplorerConditionOperatorType.IsNotEmpty; - public value: string[]; + public value: [number, number, number, number]; - constructor(operator: GeoLocationConditionOperator, value: string[]) { + constructor(operator: GeoLocationConditionOperator, value: [number, number, number, number]) { this.operator = operator; - this.value = value; + this.value = [ + value[0] ?? TransactionExplorerGeoLocationCondition.MIN_LATITUDE, + value[1] ?? TransactionExplorerGeoLocationCondition.MAX_LATITUDE, + value[2] ?? TransactionExplorerGeoLocationCondition.MIN_LONGITUDE, + value[3] ?? TransactionExplorerGeoLocationCondition.MAX_LONGITUDE + ]; } - public getValueForStore(): string[] { - if (this.operator === TransactionExplorerConditionOperatorType.IsEmpty || this.operator === TransactionExplorerConditionOperatorType.IsNotEmpty) { - return []; - } - - return []; + public getValueForStore(): [number, number, number, number] { + return [ + this.value[0] ?? TransactionExplorerGeoLocationCondition.MIN_LATITUDE, + this.value[1] ?? TransactionExplorerGeoLocationCondition.MAX_LATITUDE, + this.value[2] ?? TransactionExplorerGeoLocationCondition.MIN_LONGITUDE, + this.value[3] ?? TransactionExplorerGeoLocationCondition.MAX_LONGITUDE + ]; } public match(transaction: TransactionInsightDataItem): boolean { @@ -1014,6 +1032,62 @@ export class TransactionExplorerGeoLocationCondition implements TransactionExplo return !transaction.geoLocation; } else if (this.operator === TransactionExplorerConditionOperatorType.IsNotEmpty) { return !!transaction.geoLocation; + } else if (this.operator === TransactionExplorerConditionOperatorType.LatitudeBetween) { + if (!transaction.geoLocation) { + return false; + } + + const latitude = transaction.geoLocation.latitude; + + if (typeof(this.value[0]) === 'number' && latitude < this.value[0]) { + return false; + } + + if (typeof(this.value[1]) === 'number' && latitude > this.value[1]) { + return false; + } + + return true; + } else if (this.operator === TransactionExplorerConditionOperatorType.LatitudeNotBetween) { + if (!transaction.geoLocation) { + return false; + } + + const latitude = transaction.geoLocation.latitude; + + if (typeof(this.value[0]) === 'number' && typeof(this.value[1]) === 'number' && latitude >= this.value[0] && latitude <= this.value[1]) { + return false; + } + + return true; + } else if (this.operator === TransactionExplorerConditionOperatorType.LongitudeBetween) { + if (!transaction.geoLocation) { + return false; + } + + const longitude = transaction.geoLocation.longitude; + + if (typeof(this.value[2]) === 'number' && longitude < this.value[2]) { + return false; + } + + if (typeof(this.value[3]) === 'number' && longitude > this.value[3]) { + return false; + } + + return true; + } else if (this.operator === TransactionExplorerConditionOperatorType.LongitudeNotBetween) { + if (!transaction.geoLocation) { + return false; + } + + const longitude = transaction.geoLocation.longitude; + + if (typeof(this.value[2]) === 'number' && typeof(this.value[3]) === 'number' && longitude >= this.value[2] && longitude <= this.value[3]) { + return false; + } + + return true; } return false; @@ -1024,6 +1098,24 @@ export class TransactionExplorerGeoLocationCondition implements TransactionExplo return `geo_location IS EMPTY`; } else if (this.operator === TransactionExplorerConditionOperatorType.IsNotEmpty) { return `geo_location IS NOT EMPTY`; + } else if (this.operator === TransactionExplorerConditionOperatorType.LatitudeBetween || this.operator === TransactionExplorerConditionOperatorType.LatitudeNotBetween) { + const minLatitude: number = this.value[0] ?? TransactionExplorerGeoLocationCondition.MIN_LATITUDE; + const maxLatitude: number = this.value[1] ?? TransactionExplorerGeoLocationCondition.MAX_LATITUDE; + + if (this.operator === TransactionExplorerConditionOperatorType.LatitudeBetween) { + return `(geo_location.latitude >= ${minLatitude} AND geo_location.latitude <= ${maxLatitude})`; + } else if (this.operator === TransactionExplorerConditionOperatorType.LatitudeNotBetween) { + return `(geo_location.latitude < ${minLatitude} OR geo_location.latitude > ${maxLatitude})`; + } + } else if (this.operator === TransactionExplorerConditionOperatorType.LongitudeBetween || this.operator === TransactionExplorerConditionOperatorType.LongitudeNotBetween) { + const minLongitude: number = this.value[2] ?? TransactionExplorerGeoLocationCondition.MIN_LONGITUDE; + const maxLongitude: number = this.value[3] ?? TransactionExplorerGeoLocationCondition.MAX_LONGITUDE; + + if (this.operator === TransactionExplorerConditionOperatorType.LongitudeBetween) { + return `(geo_location.longitude >= ${minLongitude} AND geo_location.longitude <= ${maxLongitude})`; + } else if (this.operator === TransactionExplorerConditionOperatorType.LongitudeNotBetween) { + return `(geo_location.longitude < ${minLongitude} OR geo_location.longitude > ${maxLongitude})`; + } } return ''; diff --git a/src/views/desktop/insights/tabs/ExplorerQueryTab.vue b/src/views/desktop/insights/tabs/ExplorerQueryTab.vue index b084af02..c18f2b4c 100644 --- a/src/views/desktop/insights/tabs/ExplorerQueryTab.vue +++ b/src/views/desktop/insights/tabs/ExplorerQueryTab.vue @@ -236,10 +236,57 @@ /> - +
+ + + + ~ + + +