diff --git a/CMakeLists.txt b/CMakeLists.txt
index 7ead4fab..773c2de3 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -20,7 +20,7 @@ message("Building for ${CMAKE_SYSTEM_NAME}.")
FetchContent_Declare(mapget
GIT_REPOSITORY "https://github.com/Klebert-Engineering/mapget"
- GIT_TAG "v2024.3.1"
+ GIT_TAG "main"
GIT_SHALLOW ON)
FetchContent_MakeAvailable(mapget)
diff --git a/erdblick_app/app/app.component.ts b/erdblick_app/app/app.component.ts
index ac77e639..0fb71521 100644
--- a/erdblick_app/app/app.component.ts
+++ b/erdblick_app/app/app.component.ts
@@ -1,8 +1,8 @@
import {Component} from '@angular/core';
import {HttpClient} from "@angular/common/http";
+import {ActivatedRoute, NavigationEnd, Params, Router} from "@angular/router";
import {JumpTargetService} from "./jump.service";
import {MapService} from "./map.service";
-import {ActivatedRoute, NavigationEnd, Params, Router} from "@angular/router";
import {ParametersService} from "./parameters.service";
import {StyleService} from "./style.service";
import {filter} from "rxjs";
@@ -14,8 +14,8 @@ import {filter} from "rxjs";
-
+
{{ title }} {{ version }}
diff --git a/erdblick_app/app/app.module.ts b/erdblick_app/app/app.module.ts
index 8c3ac1d7..407b57c7 100644
--- a/erdblick_app/app/app.module.ts
+++ b/erdblick_app/app/app.module.ts
@@ -3,7 +3,7 @@ import {BrowserModule} from '@angular/platform-browser';
import {AppRoutingModule} from './app-routing.module';
import {AppComponent} from './app.component';
-import {HttpClientModule} from "@angular/common/http";
+import {provideHttpClient, withInterceptorsFromDi} from "@angular/common/http";
import {SpeedDialModule} from "primeng/speeddial";
import {DialogModule} from "primeng/dialog";
import {BrowserAnimationsModule} from "@angular/platform-browser/animations";
@@ -27,8 +27,11 @@ import {MapService} from "./map.service";
import {InputSwitchModule} from "primeng/inputswitch";
import {SliderModule} from "primeng/slider";
import {StyleService} from "./style.service";
+import {FeatureSearchComponent} from "./feature.search.component";
import {MapPanelComponent} from "./map.panel.component";
import {InspectionPanelComponent} from "./inspection.panel.component";
+import {FeaturePanelComponent} from "./feature.panel.component";
+import {SourceDataPanelComponent} from "./sourcedata.panel.component";
import {InspectionService} from "./inspection.service";
import {ParametersService} from "./parameters.service";
import {PreferencesComponent} from "./preferences.component";
@@ -43,12 +46,17 @@ import {SidePanelService} from "./sidepanel.service";
import {MenuModule} from "primeng/menu";
import {CardModule} from "primeng/card";
import {CoordinatesService} from "./coordinates.service";
-import {FeatureSearchComponent} from "./feature.search.component";
import {ColorPickerModule} from "primeng/colorpicker";
import {ListboxModule} from "primeng/listbox";
import {FeatureSearchService} from "./feature.search.service";
import {ClipboardService} from "./clipboard.service";
import {MultiSelectModule} from "primeng/multiselect";
+import {ButtonGroupModule} from "primeng/buttongroup";
+import {TabViewModule} from "primeng/tabview";
+import {BreadcrumbModule} from "primeng/breadcrumb";
+import {TableModule} from "primeng/table";
+import {HighlightSearch} from "./highlight.pipe";
+import {TreeTableFilterPatchDirective} from "./treetablefilter-patch.directive";
import {InputTextareaModule} from "primeng/inputtextarea";
export function initializeServices(styleService: StyleService, mapService: MapService, coordService: CoordinatesService) {
@@ -66,19 +74,25 @@ export function initializeServices(styleService: StyleService, mapService: MapSe
SearchPanelComponent,
MapPanelComponent,
InspectionPanelComponent,
+ FeaturePanelComponent,
+ SourceDataPanelComponent,
PreferencesComponent,
EditorComponent,
ErdblickViewComponent,
CoordinatesPanelComponent,
FeatureSearchComponent,
- AlertDialogComponent
+ AlertDialogComponent,
+ HighlightSearch,
+ TreeTableFilterPatchDirective,
+ ],
+ bootstrap: [
+ AppComponent
],
imports: [
BrowserModule,
BrowserAnimationsModule,
AnimateModule,
AppRoutingModule,
- HttpClientModule,
SpeedDialModule,
DialogModule,
FormsModule,
@@ -102,14 +116,18 @@ export function initializeServices(styleService: StyleService, mapService: MapSe
ColorPickerModule,
ListboxModule,
MultiSelectModule,
- InputTextareaModule
+ InputTextareaModule,
+ ButtonGroupModule,
+ TabViewModule,
+ BreadcrumbModule,
+ TableModule
],
providers: [
{
provide: APP_INITIALIZER,
useFactory: initializeServices,
deps: [StyleService, MapService, CoordinatesService],
- multi: true
+ multi: true,
},
MapService,
MessageService,
@@ -119,9 +137,8 @@ export function initializeServices(styleService: StyleService, mapService: MapSe
ParametersService,
SidePanelService,
FeatureSearchService,
- ClipboardService
- ],
- bootstrap: [AppComponent]
-})
+ ClipboardService,
+ provideHttpClient(withInterceptorsFromDi()),
+ ] })
export class AppModule {
}
diff --git a/erdblick_app/app/coordinates.service.ts b/erdblick_app/app/coordinates.service.ts
index c742a733..d7fde1ef 100644
--- a/erdblick_app/app/coordinates.service.ts
+++ b/erdblick_app/app/coordinates.service.ts
@@ -53,4 +53,4 @@ export class CoordinatesService {
}
});
}
-}
\ No newline at end of file
+}
diff --git a/erdblick_app/app/feature.panel.component.ts b/erdblick_app/app/feature.panel.component.ts
new file mode 100644
index 00000000..eb20623b
--- /dev/null
+++ b/erdblick_app/app/feature.panel.component.ts
@@ -0,0 +1,404 @@
+import {Component, Input, OnInit, ViewChild} from "@angular/core";
+import {MenuItem, TreeNode, TreeTableNode} from "primeng/api";
+import {InspectionService} from "./inspection.service";
+import {JumpTargetService} from "./jump.service";
+import {Menu} from "primeng/menu";
+import {MapService} from "./map.service";
+import {distinctUntilChanged} from "rxjs";
+import {coreLib} from "./wasm";
+import {ClipboardService} from "./clipboard.service";
+import {TreeTable} from "primeng/treetable";
+
+interface Column {
+ field: string;
+ header: string;
+}
+
+@Component({
+ selector: 'feature-panel',
+ template: `
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ rowData['key'] }}
+
+
+
+
+
+
+
+
+ |
+
+
+
+ {{ rowData['value'] }}
+
+
+
+
+
+
+ |
+
+
+
+
+ No entries found. |
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ `,
+ styles: [`
+ .section-style {
+ background-color: gainsboro;
+ margin-top: 1em;
+ }
+
+ .feature-id-style {
+ cursor: pointer;
+ text-decoration: underline dotted;
+ font-style: italic;
+ }
+
+ .source-data-ref-container {
+ button {
+ width: 20px;
+ height: 20px;
+ padding: 3px;
+ margin-bottom: 0.5px;
+ }
+ }
+
+ @media only screen and (max-width: 56em) {
+ .resizable-container-expanded {
+ height: calc(100vh - 3em);;
+ }
+ }
+ `]
+})
+export class FeaturePanelComponent implements OnInit {
+
+ //jsonTree: string = "";
+ filteredTree: TreeTableNode[] = [];
+ cols: Column[] = [];
+ isExpanded: boolean = false;
+ tooltipOptions = {
+ showDelay: 1000,
+ autoHide: false
+ };
+ filterByKeys = true;
+ filterByValues = true;
+ filterOnlyFeatureIds = false;
+ filterGeometryEntries = false;
+ jsonTree = "";
+
+ @ViewChild('tt') table!: TreeTable;
+
+ @ViewChild('inspectionMenu') inspectionMenu!: Menu;
+ inspectionMenuItems: MenuItem[] | undefined;
+ inspectionMenuVisible: boolean = false;
+
+ constructor(private clipboardService: ClipboardService,
+ public inspectionService: InspectionService,
+ public jumpService: JumpTargetService,
+ public mapService: MapService) {
+ this.inspectionService.featureTree.pipe(distinctUntilChanged()).subscribe((tree: string) => {
+ this.jsonTree = tree;
+ this.filteredTree = tree ? JSON.parse(tree) : [];
+ this.expandTreeNodes(this.filteredTree);
+ if (this.inspectionService.featureTreeFilterValue) {
+ this.filterTree();
+ }
+ });
+
+ this.inspectionService.inspectionPanelChanged.subscribe(() => {
+ // We have to force recalculate the tables number of visible items
+ setTimeout(() => {
+ let scroller = (
this.table.scrollableViewChild)?.scroller;
+ if (scroller) {
+ scroller.init();
+ scroller.calculateAutoSize();
+ }
+ }, 0);
+ })
+ }
+
+ ngOnInit(): void {
+ this.cols = [
+ { field: 'key', header: 'Key' },
+ { field: 'value', header: 'Value' }
+ ];
+ }
+
+ copyToClipboard(text: string) {
+ this.clipboardService.copyToClipboard(text);
+ }
+
+ expandTreeNodes(nodes: TreeTableNode[], parent: any = null): void {
+ nodes.forEach(node => {
+ const isTopLevelNode = parent === null;
+ const hasSingleChild = node.children && node.children.length === 1;
+ node.expanded = isTopLevelNode || hasSingleChild;
+
+ if (node.children) {
+ this.expandTreeNodes(node.children, node);
+ }
+ });
+ }
+
+ filterTree() {
+ const query = this.inspectionService.featureTreeFilterValue.toLowerCase();
+ if (!query) {
+ this.filteredTree = JSON.parse(this.jsonTree);
+ this.expandTreeNodes(this.filteredTree);
+ return;
+ }
+
+ if (this.filterOnlyFeatureIds) {
+ this.filterByKeys = false;
+ this.filterByValues = false;
+ this.filterGeometryEntries = false;
+ }
+
+ const filterNodes = (nodes: TreeTableNode[]): TreeTableNode[] => {
+ return nodes.reduce((filtered, node) => {
+ let matches = false;
+ if (!this.filterGeometryEntries && node.data.key == "Geometry") {
+ return filtered;
+ }
+
+ if (this.filterOnlyFeatureIds) {
+ if (node.data.type == this.InspectionValueType.FEATUREID.value) {
+ matches = String(node.data.value).toLowerCase().includes(query) || String(node.data.hoverId).toLowerCase().includes(query);
+ }
+ } else {
+ if (this.filterByKeys && this.filterByValues) {
+ matches = String(node.data.key).toLowerCase().includes(query) || String(node.data.value).toLowerCase().includes(query);
+ } else if (this.filterByKeys) {
+ matches = String(node.data.key).toLowerCase().includes(query);
+ } else if (this.filterByValues) {
+ matches = String(node.data.value).toLowerCase().includes(query);
+ }
+ }
+
+ if (node.children) {
+ let filteredChildren = filterNodes(node.children);
+ // node.children = filterNodes(node.children);
+ matches = matches || filteredChildren.length > 0;
+ if (matches) {
+ node.expanded = true;
+ }
+ }
+
+ if (matches) {
+ filtered.push(node);
+ }
+
+ return filtered;
+ }, []);
+ };
+
+ this.filteredTree = filterNodes(JSON.parse(this.jsonTree));
+ }
+
+ onRowClick(rowNode: any) {
+ const node: TreeNode = rowNode.node;
+ node.expanded = !node.expanded;
+ this.filteredTree = [...this.filteredTree];
+ }
+
+ onKeyClick(event: MouseEvent, rowData: any) {
+ this.inspectionMenu.toggle(event);
+ event.stopPropagation();
+ const key = rowData["key"];
+ const value = rowData["value"];
+ this.inspectionMenuItems = [
+ // {
+ // label: 'Find Features with this Value',
+ // command: () => {
+ //
+ // }
+ // },
+ {
+ label: 'Copy Key/Value',
+ command: () => {
+ this.copyToClipboard(`{${key}: ${value}}`);
+ }
+ },
+ // {
+ // label: 'Show in NDS.Live Blob',
+ // command: () => {
+ // }
+ // },
+ {
+ label: 'Open NDS.Live Docs',
+ command: () => {
+ window.open(`https://doc.nds.live/search?q=${key}`, "_blank");
+ }
+ }
+ ];
+ if (rowData.hasOwnProperty("geoJsonPath")) {
+ const path = rowData["geoJsonPath"];
+ this.inspectionMenuItems.push({
+ label: 'Copy GeoJson Path',
+ command: () => {
+ this.copyToClipboard(path);
+ }
+ });
+ }
+ }
+
+ showSourceData(sourceDataRef: any) {
+ const layerId = sourceDataRef.layerId;
+ const tileId = sourceDataRef.tileId;
+ const address = sourceDataRef.address;
+ const mapId = this.inspectionService.selectedMapIdName;
+ const featureId = this.inspectionService.selectedFeatureIdName;
+
+ this.inspectionService.selectedSourceData.next({
+ tileId: Number(tileId),
+ layerId: String(layerId),
+ mapId: String(mapId),
+ address: BigInt(address),
+ featureId: featureId,
+ })
+ }
+
+ onValueClick(event: any, rowData: any) {
+ event.stopPropagation();
+ const selection = window.getSelection();
+ if (selection && selection.toString().length > 0) {
+ return;
+ }
+
+ if (rowData["type"] == this.InspectionValueType.FEATUREID.value) {
+ this.jumpService.highlightFeature(this.inspectionService.selectedMapIdName, rowData["value"]).then();
+ }
+ this.copyToClipboard(rowData["value"]);
+ }
+
+ highlightFeature(rowData: any) {
+ return;
+ }
+
+ stopHighlight(rowData: any) {
+ return;
+ }
+
+ getStyleClassByType(valueType: number): string {
+ switch (valueType) {
+ case this.InspectionValueType.SECTION.value:
+ return "section-style"
+ case this.InspectionValueType.FEATUREID.value:
+ return "feature-id-style"
+ default:
+ return "standard-style"
+ }
+ }
+
+ protected readonly InspectionValueType = coreLib.ValueType;
+
+ clearFilter() {
+ this.inspectionService.featureTreeFilterValue = "";
+ this.filterTree();
+ }
+
+ onKeydown(event: KeyboardEvent) {
+ if (event.key === 'Escape') {
+ this.clearFilter();
+ }
+ }
+}
diff --git a/erdblick_app/app/features.model.ts b/erdblick_app/app/features.model.ts
index 810927d5..f3cb9c18 100644
--- a/erdblick_app/app/features.model.ts
+++ b/erdblick_app/app/features.model.ts
@@ -52,6 +52,9 @@ export class FeatureTile {
// Deserialize the WASM tileFeatureLayer from the blob.
return uint8ArrayToWasm((bufferToRead: any) => {
let deserializedLayer = this.parser.readTileFeatureLayer(bufferToRead);
+ if (!deserializedLayer)
+ return null;
+
// Run the callback with the deserialized layer, and
// provide the result as the return value.
let result = null;
@@ -70,6 +73,9 @@ export class FeatureTile {
// Deserialize the WASM tileFeatureLayer from the blob.
return await uint8ArrayToWasmAsync(async (bufferToRead: any) => {
let deserializedLayer = this.parser.readTileFeatureLayer(bufferToRead);
+ if (!deserializedLayer)
+ return null;
+
// Run the callback with the deserialized layer, and
// provide the result as the return value.
let result = null;
diff --git a/erdblick_app/app/fetch.model.ts b/erdblick_app/app/fetch.model.ts
index 194c8d7a..a4227960 100644
--- a/erdblick_app/app/fetch.model.ts
+++ b/erdblick_app/app/fetch.model.ts
@@ -8,6 +8,7 @@ export class Fetch
static CHUNK_HEADER_SIZE = 11;
static CHUNK_TYPE_FIELDS = 1;
static CHUNK_TYPE_FEATURES = 2;
+ static CHUNK_TYPE_SOURCEDATA = 3;
static CHUNK_TYPE_END_OF_STREAM = 128;
private url: string;
private method: string;
@@ -88,7 +89,7 @@ export class Fetch
/**
* Method to start the fetch request and process the response.
*/
- go() {
+ async go() {
let requestOptions: Record = {
method: this.method,
signal: this.abortController.signal,
@@ -102,18 +103,18 @@ export class Fetch
}
requestOptions["headers"] = headers
- fetch(this.url, requestOptions)
+ return fetch(this.url, requestOptions)
.then(response => {
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
} else {
if (this.jsonCallback) {
console.assert(!this.processChunks)
- this.handleJsonResponse(response);
+ return this.handleJsonResponse(response);
} else if (this.processChunks) {
- this.handleChunkedResponse(response).then(_ => {}).catch(e => this.handleError(e));
+ return this.handleChunkedResponse(response).then(_ => {}).catch(e => this.handleError(e));
} else {
- this.handleBlobResponse(response);
+ return this.handleBlobResponse(response);
}
}
})
@@ -124,8 +125,8 @@ export class Fetch
* Method to handle and process a Blob response.
* @param {Response} response - The fetch response.
*/
- private handleBlobResponse(response: Response) {
- response.blob()
+ private async handleBlobResponse(response: Response) {
+ return response.blob()
.then(blob => {
this.processBlob(blob);
});
@@ -193,8 +194,8 @@ export class Fetch
* Method to handle and process a JSON response.
* @param {Response} response - The fetch response.
*/
- handleJsonResponse(response: Response) {
- response.json()
+ private async handleJsonResponse(response: Response) {
+ return response.json()
.then(jsonData => {
// Serialize the JSON before it is passed to the callback, so that
// any manipulations to it will not side-effect a later buffer callback.
diff --git a/erdblick_app/app/highlight.pipe.ts b/erdblick_app/app/highlight.pipe.ts
new file mode 100644
index 00000000..5b0b972f
--- /dev/null
+++ b/erdblick_app/app/highlight.pipe.ts
@@ -0,0 +1,15 @@
+import { Pipe, PipeTransform } from '@angular/core';
+
+@Pipe({
+ name: 'highlight'
+})
+export class HighlightSearch implements PipeTransform {
+ transform(value: any, args: any): any {
+ if (!args || !value || typeof value === 'object') {
+ return value;
+ }
+
+ let re = new RegExp(String(args).replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'gi');
+ return String(value).replace(re, '$&');
+ }
+}
\ No newline at end of file
diff --git a/erdblick_app/app/inspection.panel.component.ts b/erdblick_app/app/inspection.panel.component.ts
index 306712f8..334055fd 100644
--- a/erdblick_app/app/inspection.panel.component.ts
+++ b/erdblick_app/app/inspection.panel.component.ts
@@ -1,350 +1,153 @@
-import {Component, OnInit, ViewChild} from "@angular/core";
-import {MenuItem, TreeNode, TreeTableNode} from "primeng/api";
-import {InspectionService} from "./inspection.service";
-import {JumpTargetService} from "./jump.service";
-import {Menu} from "primeng/menu";
-import {MapService} from "./map.service";
-import {distinctUntilChanged, filter} from "rxjs";
-import {coreLib} from "./wasm";
-import {ClipboardService} from "./clipboard.service";
-
-interface Column {
- field: string;
- header: string;
+import {Component, OnInit} from "@angular/core";
+import {InspectionService, SelectedSourceData, selectedSourceDataEqualTo} from "./inspection.service";
+import {distinctUntilChanged} from "rxjs";
+import {FeaturePanelComponent} from "./feature.panel.component";
+import {SourceDataPanelComponent} from "./sourcedata.panel.component";
+import {ParametersService} from "./parameters.service";
+
+interface InspectorTab {
+ title: string,
+ icon: string,
+ component: any,
+ inputs?: Record,
+ onClose?: any,
}
@Component({
selector: 'inspection-panel',
template: `
+ class="w-full inspect-panel"
+ [activeIndex]="0">
-
-
- {{ inspectionService.selectedFeatureIdName }}
-
+
+ 0" (click)="onGoBack($event)" />
+
+ {{ tabs[activeIndex].title || '' }}
+
+
-
-
-
-
-
+
+
+
-
-
-
-
-
-
-
- {{ rowData['key'] }}
-
- |
-
-
-
- {{ rowData['value'] }}
-
-
-
-
-
-
- |
-
-
-
-
- No entries found. |
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
`,
- styles: [`
- .section-style {
- background-color: gainsboro;
- margin-top: 1em;
- }
-
- .feature-id-style {
- cursor: pointer;
- text-decoration: underline dotted;
- font-style: italic;
- }
-
- @media only screen and (max-width: 56em) {
- .resizable-container-expanded {
- height: calc(100vh - 3em);;
+ styles: [
+ `@layer erdblick {
+ .inspector-title {
+ display: flex;
+ gap: 4px;
+ justify-content: center;
+ align-items: center;
+
+ .p-button {
+ width: 30px;
+ height: 30px;
+ margin: 0;
+ }
}
- }
- `]
+ }`,
+ ]
})
-export class InspectionPanelComponent implements OnInit {
+export class InspectionPanelComponent implements OnInit
+{
+ title = "";
+ tabs: InspectorTab[] = [];
+ activeIndex = 0;
- jsonTree: string = "";
- filteredTree: TreeTableNode[] = [];
- cols: Column[] = [];
- isExpanded: boolean = false;
- tooltipOptions = {
- showDelay: 1000,
- autoHide: false
- };
- filterByKeys = true;
- filterByValues = true;
- filterOnlyFeatureIds = false;
- filterGeometryEntries = false;
+ constructor(public inspectionService: InspectionService, private parameterService: ParametersService) {
+ this.pushFeatureInspector();
- @ViewChild('inspectionMenu') inspectionMenu!: Menu;
- inspectionMenuItems: MenuItem[] | undefined;
- inspectionMenuVisible: boolean = false;
-
- constructor(private clipboardService: ClipboardService,
- public inspectionService: InspectionService,
- public jumpService: JumpTargetService,
- public mapService: MapService) {
this.inspectionService.featureTree.pipe(distinctUntilChanged()).subscribe((tree: string) => {
- this.jsonTree = tree;
- this.filteredTree = tree ? JSON.parse(tree) : [];
- this.expandTreeNodes(this.filteredTree);
- if (this.inspectionService.featureTreeFilterValue) {
- this.filterTree();
- }
+ this.reset();
+
+ // TODO: Create a new FeaturePanelComponent instance for each unique selected feature
+ // then we can get rid of all the service's View Component logic/functions.
+ // reset() Would then completely clear the tabs.
+ const featureId = this.inspectionService.selectedFeatureIdName;
+ this.tabs[0].title = featureId;
+
+ const selectedSourceData = parameterService.getSelectedSourceData()
+ if (selectedSourceData?.featureId === featureId)
+ this.inspectionService.selectedSourceData.next(selectedSourceData);
+ else
+ this.inspectionService.selectedSourceData.next(null);
});
- }
- ngOnInit(): void {
- this.cols = [
- { field: 'key', header: 'Key' },
- { field: 'value', header: 'Value' }
- ];
+ this.inspectionService.selectedSourceData.pipe(distinctUntilChanged(selectedSourceDataEqualTo)).subscribe(selection => {
+ if (selection)
+ this.pushSourceDataInspector(selection);
+ })
}
- copyToClipboard(text: string) {
- this.clipboardService.copyToClipboard(text);
- }
-
- expandTreeNodes(nodes: TreeTableNode[], parent: any = null): void {
- nodes.forEach(node => {
- const isTopLevelNode = parent === null;
- const hasSingleChild = node.children && node.children.length === 1;
- node.expanded = isTopLevelNode || hasSingleChild;
+ ngOnInit(): void {}
- if (node.children) {
- this.expandTreeNodes(node.children, node);
- }
- });
- }
-
- filterTree() {
- const query = this.inspectionService.featureTreeFilterValue.toLowerCase();
- if (!query) {
- this.filteredTree = JSON.parse(this.jsonTree);
- this.expandTreeNodes(this.filteredTree);
- return;
+ reset() {
+ /* We always keep the first tab, which is a feature inspector. */
+ this.setTab(0);
+ for (let i = 1; i < this.tabs.length - 1; ++i) {
+ let close = this.tabs[this.tabs.length - i]['onClose']
+ if (close)
+ close();
}
-
- if (this.filterOnlyFeatureIds) {
- this.filterByKeys = false;
- this.filterByValues = false;
- this.filterGeometryEntries = false;
+ if (this.tabs.length > 0) {
+ this.tabs = [this.tabs[0]!];
}
-
- const filterNodes = (nodes: TreeTableNode[]): TreeTableNode[] => {
- return nodes.reduce
((filtered, node) => {
- let matches = false;
- if (!this.filterGeometryEntries && node.data.key == "Geometry") {
- return filtered;
- }
-
- if (this.filterOnlyFeatureIds) {
- if (node.data.type == this.InspectionValueType.FEATUREID.value) {
- matches = String(node.data.value).toLowerCase().includes(query) || String(node.data.hoverId).toLowerCase().includes(query);
- }
- } else {
- if (this.filterByKeys && this.filterByValues) {
- matches = String(node.data.key).toLowerCase().includes(query) || String(node.data.value).toLowerCase().includes(query);
- } else if (this.filterByKeys) {
- matches = String(node.data.key).toLowerCase().includes(query);
- } else if (this.filterByValues) {
- matches = String(node.data.value).toLowerCase().includes(query);
- }
- }
-
- if (node.children) {
- let filteredChildren = filterNodes(node.children);
- // node.children = filterNodes(node.children);
- matches = matches || filteredChildren.length > 0;
- if (matches) {
- node.expanded = true;
- }
- }
-
- if (matches) {
- filtered.push(node);
- }
-
- return filtered;
- }, []);
- };
-
- this.filteredTree = filterNodes(JSON.parse(this.jsonTree));
}
- onRowClick(rowNode: any) {
- const node: TreeNode = rowNode.node;
- node.expanded = !node.expanded;
- this.filteredTree = [...this.filteredTree];
- }
-
- onKeyClick(event: MouseEvent, rowData: any) {
- this.inspectionMenu.toggle(event);
- event.stopPropagation();
- const key = rowData["key"];
- const value = rowData["value"];
- this.inspectionMenuItems = [
- // {
- // label: 'Find Features with this Value',
- // command: () => {
- //
- // }
- // },
- {
- label: 'Copy Key/Value',
- command: () => {
- this.copyToClipboard(`{${key}: ${value}}`);
- }
+ pushFeatureInspector() {
+ let tab = {
+ title: "",
+ icon: "pi-sitemap",
+ component: FeaturePanelComponent,
+ onClose: () => {
+ this.inspectionService.featureTree.next("");
},
- // {
- // label: 'Show in NDS.Live Blob',
- // command: () => {
- // }
- // },
- {
- label: 'Open NDS.Live Docs',
- command: () => {
- window.open(`https://doc.nds.live/search?q=${key}`, "_blank");
- }
- }
- ];
- if (rowData.hasOwnProperty("geoJsonPath")) {
- const path = rowData["geoJsonPath"];
- this.inspectionMenuItems.push({
- label: 'Copy GeoJson Path',
- command: () => {
- this.copyToClipboard(path);
- }
- });
- }
- }
-
- onValueClick(event: any, rowData: any) {
- event.stopPropagation();
- const selection = window.getSelection();
- if (selection && selection.toString().length > 0) {
- return;
- }
-
- if (rowData["type"] == this.InspectionValueType.FEATUREID.value) {
- this.jumpService.highlightFeature(this.inspectionService.selectedMapIdName, rowData["value"]).then();
}
- this.copyToClipboard(rowData["value"]);
- }
-
- highlightFeature(rowData: any) {
- return;
- }
- stopHighlight(rowData: any) {
- return;
+ this.tabs = [...this.tabs, tab];
+ this.setTab(-1);
}
- getStyleClassByType(valueType: number): string {
- switch (valueType) {
- case this.InspectionValueType.SECTION.value:
- return "section-style"
- case this.InspectionValueType.FEATUREID.value:
- return "feature-id-style"
- default:
- return "standard-style"
+ pushSourceDataInspector(data: SelectedSourceData) {
+ let tab = {
+ title: data.layerId,
+ icon: "pi-database",
+ component: SourceDataPanelComponent,
+ inputs: {
+ sourceData: data
+ },
+ onClose: () => {
+ this.inspectionService.selectedSourceData.next(null);
+ },
}
- }
- protected readonly InspectionValueType = coreLib.ValueType;
+ this.tabs = [...this.tabs, tab];
+ this.setTab(-1);
+ }
- clearFilter() {
- this.inspectionService.featureTreeFilterValue = "";
- this.filterTree();
+ setTab(index: number) {
+ if (index < 0)
+ index = this.tabs.length - 1;
+ this.inspectionService.inspectionPanelChanged.emit();
+ this.activeIndex = Math.max(0, index)
}
- onKeydown(event: KeyboardEvent) {
- if (event.key === 'Escape') {
- this.clearFilter();
+ onGoBack(event: any) {
+ event.stopPropagation();
+ if (this.activeIndex > 0) {
+ const onClose = this.tabs[this.activeIndex]['onClose'];
+ if (onClose)
+ onClose();
+ this.setTab(this.activeIndex - 1);
+ if (this.tabs.length > 1)
+ this.tabs.pop();
}
}
-}
\ No newline at end of file
+}
diff --git a/erdblick_app/app/inspection.service.ts b/erdblick_app/app/inspection.service.ts
index 30e7f8dd..2130aa46 100644
--- a/erdblick_app/app/inspection.service.ts
+++ b/erdblick_app/app/inspection.service.ts
@@ -1,12 +1,13 @@
-import {Injectable} from "@angular/core";
+import {EventEmitter, Injectable} from "@angular/core";
import {TreeTableNode} from "primeng/api";
-import {BehaviorSubject, distinctUntilChanged, filter} from "rxjs";
+import {BehaviorSubject, distinctUntilChanged, distinctUntilKeyChanged, filter, ReplaySubject} from "rxjs";
import {MapService} from "./map.service";
-import {Feature} from "../../build/libs/core/erdblick-core";
+import {Feature, TileSourceDataLayer} from "../../build/libs/core/erdblick-core";
import {FeatureWrapper} from "./features.model";
import {ParametersService} from "./parameters.service";
-import {coreLib} from "./wasm";
+import {coreLib, uint8ArrayToWasm} from "./wasm";
import {JumpTargetService} from "./jump.service";
+import {Fetch} from "./fetch.model";
interface InspectionModelData {
@@ -16,9 +17,24 @@ interface InspectionModelData {
info?: string;
hoverId?: string
geoJsonPath?: string;
+ sourceDataReferences?: Array
-
@@ -193,7 +193,6 @@ import {Menu} from "primeng/menu";
styles: [``]
})
export class MapPanelComponent {
-
editorDialogVisible: boolean = false;
layerDialogVisible: boolean = false;
warningDialogVisible: boolean = false;
@@ -484,4 +483,4 @@ export class MapPanelComponent {
unordered(a: KeyValue, b: KeyValue): number {
return 0;
}
-}
\ No newline at end of file
+}
diff --git a/erdblick_app/app/map.service.ts b/erdblick_app/app/map.service.ts
index ab2e02e0..dc1b7c21 100644
--- a/erdblick_app/app/map.service.ts
+++ b/erdblick_app/app/map.service.ts
@@ -22,7 +22,7 @@ export interface LayerInfoItem extends Object {
coverage: Array;
featureTypes: Array<{name: string, uniqueIdCompositions: Array