Skip to content

Commit

Permalink
Merge pull request #153 from ndsev/sourcedata-switch-layer
Browse files Browse the repository at this point in the history
Sourcedata switch layer
  • Loading branch information
johannes-wolf authored Aug 29, 2024
2 parents 8b8e036 + 5cd4857 commit ae679a1
Show file tree
Hide file tree
Showing 15 changed files with 212 additions and 141 deletions.
4 changes: 2 additions & 2 deletions erdblick_app/app/highlight.pipe.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export class HighlightSearch implements PipeTransform {
return value;
}

let re = new RegExp(String(args).replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'gi');
const re = new RegExp(String(args).replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'gi');
return String(value).replace(re, '<mark>$&</mark>');
}
}
}
6 changes: 2 additions & 4 deletions erdblick_app/app/inspection.panel.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ interface InspectorTab {
}`,
]
})
export class InspectionPanelComponent implements OnInit
export class InspectionPanelComponent
{
title = "";
tabs: InspectorTab[] = [];
Expand Down Expand Up @@ -86,8 +86,6 @@ export class InspectionPanelComponent implements OnInit
})
}

ngOnInit(): void {}

reset() {
/* We always keep the first tab, which is a feature inspector. */
this.setTab(0);
Expand Down Expand Up @@ -117,7 +115,7 @@ export class InspectionPanelComponent implements OnInit

pushSourceDataInspector(data: SelectedSourceData) {
let tab = {
title: data.layerId,
title: SourceDataPanelComponent.layerNameForLayerId(data.layerId),
icon: "pi-database",
component: SourceDataPanelComponent,
inputs: {
Expand Down
19 changes: 8 additions & 11 deletions erdblick_app/app/inspection.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export interface SelectedSourceData {
export function selectedSourceDataEqualTo(a: SelectedSourceData | null, b: SelectedSourceData | null) {
if (!a || !b)
return false;
return (a == b || (a.mapId == b.mapId && a.tileId == b.tileId && a.layerId == b.layerId && a.address == b.address && a.featureId == b.featureId));
return (a === b || (a.mapId === b.mapId && a.tileId === b.tileId && a.layerId === b.layerId && a.address === b.address && a.featureId === b.featureId));
}

@Injectable({providedIn: 'root'})
Expand Down Expand Up @@ -174,16 +174,13 @@ export class InspectionService {
async loadSourceDataLayer(tileId: number, layerId: string, mapId: string) : Promise<TileSourceDataLayer> {
console.log(`Loading SourceDataLayer layerId=${layerId} tileId=${tileId}`);

let requests = [{
mapId: mapId,
layerId: layerId,
tileIds: [tileId]
}];

let tileParser = new coreLib.TileLayerParser();

let newRequestBody = JSON.stringify({
requests: requests
const tileParser = new coreLib.TileLayerParser();
const newRequestBody = JSON.stringify({
requests: [{
mapId: mapId,
layerId: layerId,
tileIds: [tileId]
}]
});

let layer: TileSourceDataLayer | undefined;
Expand Down
70 changes: 36 additions & 34 deletions erdblick_app/app/map.panel.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,41 +39,43 @@ import {Menu} from "primeng/menu";
<span class="font-bold white-space-nowrap map-header">
{{ mapItem.key }}
</span>
<div *ngFor="let mapLayer of mapItem.value.layers | keyvalue: unordered" class="flex-container">
<div class="font-bold white-space-nowrap"
style="margin-left: 0.5em; display: flex; align-items: center;">
<span class="material-icons" style="font-size: 1.5em; cursor: pointer"
(click)="showLayersToggleMenu($event, mapItem.key, mapLayer.key)">more_vert</span>
<span>
<p-checkbox [(ngModel)]="mapLayer.value.visible"
(ngModelChange)="toggleLayer(mapItem.key, mapLayer.key)"
[label]="mapLayer.key" [binary]="true"/>
</span>
</div>
<div class="layer-controls">
<p-button (click)="toggleTileBorders(mapItem.key, mapLayer.key)"
label="" pTooltip="Toggle tile borders" tooltipPosition="bottom"
[style]="{'padding-left': '0', 'padding-right': '0'}">
<span class="material-icons"
style="font-size: 1.2em; margin: 0 auto;">{{ mapLayer.value.tileBorders ? 'select_all' : 'deselect' }}</span>
</p-button>
<p-button *ngIf="mapLayer.value.coverage.length"
(click)="focus(mapLayer.value.coverage[0], $event)"
label="" pTooltip="Focus on layer" tooltipPosition="bottom"
[style]="{'padding-left': '0', 'padding-right': '0'}">
<span class="material-icons" style="font-size: 1.2em; margin: 0 auto;">loupe</span>
</p-button>
<p-inputNumber [(ngModel)]="mapLayer.value.level"
(ngModelChange)="onLayerLevelChanged($event, mapItem.key, mapLayer.key)"
[showButtons]="true" [min]="0" [max]="15"
buttonLayout="horizontal" spinnerMode="horizontal" inputId="horizontal"
decrementButtonClass="p-button-secondary"
incrementButtonClass="p-button-secondary"
incrementButtonIcon="pi pi-plus" decrementButtonIcon="pi pi-minus"
pTooltip="Change zoom level" tooltipPosition="bottom">
</p-inputNumber>
<div *ngFor="let mapLayer of mapItem.value.layers | keyvalue: unordered">
<div *ngIf="mapLayer.value.type != 'SourceData'" class="flex-container">
<div class="font-bold white-space-nowrap"
style="margin-left: 0.5em; display: flex; align-items: center;">
<span class="material-icons" style="font-size: 1.5em; cursor: pointer"
(click)="showLayersToggleMenu($event, mapItem.key, mapLayer.key)">more_vert</span>
<span>
<p-checkbox [(ngModel)]="mapLayer.value.visible"
(ngModelChange)="toggleLayer(mapItem.key, mapLayer.key)"
[label]="mapLayer.key" [binary]="true"/>
</span>
</div>
<div class="layer-controls">
<p-button (click)="toggleTileBorders(mapItem.key, mapLayer.key)"
label="" pTooltip="Toggle tile borders" tooltipPosition="bottom"
[style]="{'padding-left': '0', 'padding-right': '0'}">
<span class="material-icons"
style="font-size: 1.2em; margin: 0 auto;">{{ mapLayer.value.tileBorders ? 'select_all' : 'deselect' }}</span>
</p-button>
<p-button *ngIf="mapLayer.value.coverage.length"
(click)="focus(mapLayer.value.coverage[0], $event)"
label="" pTooltip="Focus on layer" tooltipPosition="bottom"
[style]="{'padding-left': '0', 'padding-right': '0'}">
<span class="material-icons" style="font-size: 1.2em; margin: 0 auto;">loupe</span>
</p-button>
<p-inputNumber [(ngModel)]="mapLayer.value.level"
(ngModelChange)="onLayerLevelChanged($event, mapItem.key, mapLayer.key)"
[showButtons]="true" [min]="0" [max]="15"
buttonLayout="horizontal" spinnerMode="horizontal" inputId="horizontal"
decrementButtonClass="p-button-secondary"
incrementButtonClass="p-button-secondary"
incrementButtonIcon="pi pi-plus" decrementButtonIcon="pi pi-minus"
pTooltip="Change zoom level" tooltipPosition="bottom">
</p-inputNumber>
</div>
<input class="level-indicator" type="text" pInputText [disabled]="true" [(ngModel)]="mapLayer.value.level" />
</div>
<input class="level-indicator" type="text" pInputText [disabled]="true" [(ngModel)]="mapLayer.value.level" />
</div>
</div>
</div>
Expand Down
8 changes: 2 additions & 6 deletions erdblick_app/app/map.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export interface LayerInfoItem extends Object {
coverage: Array<number|CoverageRectItem>;
featureTypes: Array<{name: string, uniqueIdCompositions: Array<Object>}>;
layerId: string;
type: number;
type: string;
version: {major: number, minor: number, patch: number};
zoomLevels: Array<number>;
level: number;
Expand Down Expand Up @@ -265,10 +265,6 @@ export class MapService {
let maps = new Map<string, MapInfoItem>(result.filter(m => !m.addOn).map(mapInfo => {
let layers = new Map<string, LayerInfoItem>();
for (let [layerId, layerInfo] of Object.entries(mapInfo.layers)) {
// Filter out source-data layers
if (layerId.startsWith("SourceData-"))
continue;

[layerInfo.visible, layerInfo.level, layerInfo.tileBorders] = this.parameterService.mapLayerConfig(mapInfo.mapId, layerId, 13);
mapLayerLevels.push([
mapInfo.mapId + '/' + layerId,
Expand Down Expand Up @@ -299,7 +295,7 @@ export class MapService {

const layer = mapItem.layers.get(layerId);
if (layer) {
if (layer.type == coreLib.LayerType.SOURCEDATA.value)
if (layer.type == "SourceData")
return false;
return layer.visible;
}
Expand Down
2 changes: 1 addition & 1 deletion erdblick_app/app/parameters.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ export class ParametersService {
}
});

constructor(public router: Router /*, private inspectionService: InspectionService*/) {
constructor(public router: Router) {
let parameters = this.loadSavedParameters();
this.parameters = new BehaviorSubject<ErdblickParameters>(parameters!);
this.saveParameters();
Expand Down
98 changes: 71 additions & 27 deletions erdblick_app/app/sourcedata.panel.component.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import {Component, OnInit, Input, ViewChild} from "@angular/core";
import {TreeTableNode} from "primeng/api";
import {InspectionService, SelectedSourceData} from "./inspection.service";
import {MapService} from "./map.service";
import {coreLib} from "./wasm";
import {SourceDataAddressFormat} from "build/libs/core/erdblick-core";
import {TreeTable} from "primeng/treetable";
import {Menu} from "primeng/menu";

@Component({
selector: 'sourcedata-panel',
Expand Down Expand Up @@ -39,6 +41,7 @@ import {TreeTable} from "primeng/treetable";
/>
<i *ngIf="filterString" (click)="clearFilter()"
class="pi pi-times clear-icon" style="cursor: pointer"></i>
<p-button icon="pi pi-ellipsis-v" tooltip="Select Layer" (click)="layerMenuItemsMenu.toggle($event)" />
</div>
</ng-template>
Expand Down Expand Up @@ -77,39 +80,54 @@ import {TreeTable} from "primeng/treetable";
</div>
</div>
</ng-template>
<p-menu #layerMenuItemsMenu [model]="layerMenuItems" [popup]="true" appendTo="body" [style]="{'width': 'auto'}" />
`
})
export class SourceDataPanelComponent implements OnInit {
@Input()
sourceData!: SelectedSourceData;
@Input() sourceData!: SelectedSourceData;

@ViewChild('tt')
table!: TreeTable;
@ViewChild('tt') table!: TreeTable;
@ViewChild('layerMenuItemsMenu') layerListMenu!: Menu;

treeData: TreeTableNode[] = [];
filterFields = [
"key",
"value"
];
columns = [
{ key: "key", header: "Key", width: '180px', transform: (v: any) => v },
{ key: "key", header: "Key", width: '0*', transform: (v: any) => v },
{ key: "value", header: "Value", width: '0*', transform: (v: any) => v },
{ key: "address", header: "Address", width: '80px', transform: this.addressFormatter },
{ key: "address", header: "Address", width: '100px', transform: this.addressFormatter },
{ key: "type", header: "Type", width: 'auto', transform: this.schemaTypeURLFormatter },
]

loading: boolean = true;
filterString = "";
addressFormat: SourceDataAddressFormat = coreLib.SourceDataAddressFormat.BIT_RANGE;
errorMessage = "";
isExpanded = false;
filterString = "";

constructor(private inspectionService: InspectionService) {}
layerMenuItems: any[] = [];

/**
* Returns a human readable layer name for a layer id.
*
* @param layerId Layer id to get the name for
*/
public static layerNameForLayerId(layerId: string) {
const match = layerId.match(/^SourceData-([^.]+\.)*(.*)-([\d]+)/);
if (match)
return `${match[2]}.${match[3]}`;
return layerId;
}

constructor(private inspectionService: InspectionService, public mapService: MapService) {}

ngOnInit(): void {
this.inspectionService.loadSourceDataLayer(this.sourceData.tileId, this.sourceData.layerId, this.sourceData.mapId)
.then(layer => {
let root = layer.toObject()
const root = layer.toObject()
this.addressFormat = layer.addressFormat();

layer.delete();
Expand All @@ -128,6 +146,34 @@ export class SourceDataPanelComponent implements OnInit {
.finally(() => {
this.loading = false;
});

this.mapService.maps.subscribe(maps => {
const map = maps.get(this.sourceData.mapId);
if (map) {
this.layerMenuItems = [
{
label: "Switch Layer",
items: Array.from(map.layers.values())
.filter(item => item.type == "SourceData")
.map(item => {
return {
label: SourceDataPanelComponent.layerNameForLayerId(item.layerId),
disabled: item.layerId === this.sourceData.layerId,
command: () => {
let sourceData = {...this.sourceData};
sourceData.layerId = item.layerId;
sourceData.address = BigInt(0);

this.inspectionService.selectedSourceData.next(sourceData);
},
};
}),
},
];
} else {
this.layerMenuItems = [];
}
});
}

/**
Expand All @@ -141,7 +187,7 @@ export class SourceDataPanelComponent implements OnInit {
this.treeData = [];
this.errorMessage = message;

console.error(this.errorMessage);
console.error("Error while processing SourceData tree:", this.errorMessage);
}

/**
Expand All @@ -158,48 +204,48 @@ export class SourceDataPanelComponent implements OnInit {

const prefix = "https://developer.nds.live/schema/";

let match = schema.match(/^nds\.(([^.]+\.)+)v(\d{4}_\d{2})((\.[^.]*)+)/);
const match = schema.match(/^nds\.(([^.]+\.)+)v(\d{4}_\d{2})((\.[^.]*)+)/);
if (!match || match.length <= 4)
return schema;

// Sub-namespaces in front of the version get joined by "-". Names past the version get joined by "/"
let url =
const url =
match[1].replace(/^(.*)\.$/, "$1/").replaceAll(".", "-") +
match[3].replaceAll("_", ".") +
match[4].replaceAll(".", "/");
return `<a href="${prefix + url}" target="_blank">${schema}</a>`;
}

addressFormatter(address?: any) {
if (!address) {
return address;
}

addressFormatter(address?: any): string {
if (typeof address === 'object') {
return `${address.offset}:${address.size}`
} else {
} else if (address) {
return `${address}`
} else {
return '';
}
}

selectItemWithAddress(address: bigint) {
let searchAddress: any = address;
let addressInRange: any;
if (this.addressFormat == coreLib.SourceDataAddressFormat.BIT_RANGE) {
searchAddress = {
const searchAddress = {
offset: address >> BigInt(32) & BigInt(0xFFFFFFFF),
size: address & BigInt(0xFFFFFFFF),
}

const addressLow = typeof searchAddress === 'object' ? searchAddress['offset'] : searchAddress;
const addressHigh = addressLow + (typeof searchAddress === 'object' ? searchAddress['size'] : searchAddress);

addressInRange = (addr: any) => {
return addr.offset >= addressLow && addr.offset + addr.size <= addressHigh && (addr.size != 0 || addressLow == addressHigh);
addressInRange = (address: any) => {
return address.offset >= addressLow &&
address.offset + address.size <= addressHigh &&
(address.size != 0 || addressLow == addressHigh);
}
} else {
addressInRange = (addr: any) => {
return addr == searchAddress;
const searchAddress = address;
addressInRange = (address: any) => {
return address == searchAddress;
}
}

Expand All @@ -215,8 +261,7 @@ export class SourceDataPanelComponent implements OnInit {
node.data.styleClass = "highlight";
}

const address = node.data.address;
if (address && addressInRange(address)) {
if (node.data.address && addressInRange(node.data.address)) {
highlight = true;

if (!firstHighlightedItemIndex)
Expand All @@ -233,7 +278,6 @@ export class SourceDataPanelComponent implements OnInit {
}
};

console.log(`Highlighting item with address`, searchAddress);
this.treeData.forEach((item: TreeTableNode, index) => {
select(item, [], false, index);
});
Expand Down
Loading

0 comments on commit ae679a1

Please sign in to comment.