Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Pins Clustering WIP #140

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions erdblick_app/app/cesium.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,9 @@ export type PrimitiveCollection = Cesium.PrimitiveCollection;
export const PrimitiveCollection = Cesium.PrimitiveCollection;
export type BillboardCollection = Cesium.BillboardCollection;
export const BillboardCollection = Cesium.BillboardCollection;
export type PointPrimitiveCollection = Cesium.PointPrimitiveCollection;
export const PointPrimitiveCollection = Cesium.PointPrimitiveCollection;
export type PinBuilder = Cesium.PinBuilder;
export const PinBuilder = Cesium.PinBuilder;
export const SceneTransforms = Cesium.SceneTransforms;
export type Entity = Cesium.Entity;
export const Entity = Cesium.Entity;
export type Camera = Cesium.Camera;
Expand Down
26 changes: 14 additions & 12 deletions erdblick_app/app/feature.search.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export class FeatureSearchService {
currentQuery: string = ""
workers: Array<Worker> = []
visualization: BillboardCollection = new BillboardCollection();
visualizationPositions: Array<Cartesian3> = [];
visualizationChanged: Subject<void> = new Subject<void>();
resultsPerTile: Map<string, SearchResultForTile> = new Map<string, SearchResultForTile>();
workQueue: Array<FeatureTile> = [];
Expand Down Expand Up @@ -65,9 +66,8 @@ export class FeatureSearchService {
this.startTime = Date.now();

// Fill up work queue and start processing.
for (const [_, tile] of this.mapService.loadedTileLayers) {
this.workQueue.push(tile);
}
// TODO: What if we move / change the viewport during the search?
this.workQueue = this.mapService.getPrioritisedTiles();
this.totalTiles = this.workQueue.length;
this.isFeatureSearchActive.next(true);

Expand All @@ -92,6 +92,7 @@ export class FeatureSearchService {
this.stop();
this.currentQuery = "";
this.visualization.removeAll();
this.visualizationPositions = [];
this.resultsPerTile.clear();
this.workQueue = [];
this.totalTiles = 0;
Expand All @@ -118,15 +119,16 @@ export class FeatureSearchService {

tileResult.billboardPrimitiveIndices = [];
for (const [_, __, position] of tileResult.matches) {
tileResult.billboardPrimitiveIndices.push(this.visualization.length);
this.visualization.add({
position: position,
image: this.markerGraphics(),
width: 32,
height: 32,
pixelOffset: new Cartesian2(0, -10),
eyeOffset: new Cartesian3(0, 0, -100)
});
this.visualizationPositions.push(new Cartesian3(position.x, position.y, position.z));
tileResult.billboardPrimitiveIndices.push(this.visualizationPositions.length);
// this.visualization.add({
// position: position,
// image: this.markerGraphics(),
// width: 32,
// height: 32,
// pixelOffset: new Cartesian2(0, -10),
// eyeOffset: new Cartesian3(0, 0, -100)
// });
}
}

Expand Down
8 changes: 4 additions & 4 deletions erdblick_app/app/featurefilter.worker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ export interface SearchWorkerTask {
}

export interface SearchResultForTile {
query: string;
numFeatures: number;
matches: Array<[string, string, [number, number, number]]>; // Array of (MapTileKey, FeatureId, (x, y, z))
billboardPrimitiveIndices?: Array<number>; // Used by search service for visualization.
query: string;
numFeatures: number;
matches: Array<[string, string, {x: number, y: number, z: number}]>; // Array of (MapTileKey, FeatureId, (x, y, z))
billboardPrimitiveIndices?: Array<number>; // Used by search service for visualization.
}

addEventListener('message', async ({data}) => {
Expand Down
11 changes: 10 additions & 1 deletion erdblick_app/app/map.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ export class MapService {

public maps: BehaviorSubject<Map<string, MapInfoItem>> = new BehaviorSubject<Map<string, MapInfoItem>>(new Map<string, MapInfoItem>());
public loadedTileLayers: Map<string, FeatureTile>;
private visualizedTileLayers: Map<string, TileVisualization[]>;
public visualizedTileLayers: Map<string, TileVisualization[]>;
private currentFetch: any;
private currentViewport: ViewportProperties;
private currentVisibleTileIds: Set<bigint>;
Expand Down Expand Up @@ -599,6 +599,15 @@ export class MapService {
this.update();
}

getPrioritisedTiles() {
let tiles = new Array<[number, FeatureTile]>();
for (const [_, tile] of this.loadedTileLayers) {
tiles.push([coreLib.getTilePriorityById(this.currentViewport, tile.tileId), tile]);
}
tiles.sort((a, b) => b[0] - a[0]);
return tiles.map(val => val[1]);
}

getFeatureTile(tileKey: string): FeatureTile|null {
return this.loadedTileLayers.get(tileKey) || null;
}
Expand Down
120 changes: 118 additions & 2 deletions erdblick_app/app/view.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,10 @@ import {
ScreenSpaceEventType,
UrlTemplateImageryProvider,
Viewer,
HeightReference
HeightReference,
PinBuilder,
BillboardCollection,
SceneTransforms
} from "./cesium";
import {ParametersService} from "./parameters.service";
import {AfterViewInit, Component} from "@angular/core";
Expand Down Expand Up @@ -51,6 +54,7 @@ export class ErdblickViewComponent implements AfterViewInit {
private tileVisForPrimitive: Map<any, TileVisualization>;
private openStreetMapLayer: ImageryLayer | null = null;
private marker: Entity | null = null;
private pinBuilder: PinBuilder | null = null;

/**
* Construct a Cesium View with a Model.
Expand Down Expand Up @@ -127,6 +131,8 @@ export class ErdblickViewComponent implements AfterViewInit {
}
);

this.pinBuilder = new PinBuilder();

this.openStreetMapLayer = this.viewer.imageryLayers.addImageryProvider(this.getOpenStreetMapLayerProvider());
this.openStreetMapLayer.alpha = 0.3;

Expand Down Expand Up @@ -203,8 +209,11 @@ export class ErdblickViewComponent implements AfterViewInit {

this.viewer.scene.primitives.add(this.featureSearchService.visualization);
this.featureSearchService.visualizationChanged.subscribe(_ => {
this.addBillboards();
this.viewer.scene.requestRender();
});
// Set up camera event handlers for clustering
this.setupCameraHandlers();

this.jumpService.markedPosition.subscribe(position => {
if (position.length >= 2) {
Expand Down Expand Up @@ -384,7 +393,6 @@ export class ErdblickViewComponent implements AfterViewInit {
}
}


addMarker(cartesian: Cartesian3) {
if (this.marker) {
this.viewer.entities.remove(this.marker);
Expand All @@ -402,4 +410,112 @@ export class ErdblickViewComponent implements AfterViewInit {
}
});
}

addBillboards() {
this.featureSearchService.visualization.removeAll();

this.featureSearchService.visualizationPositions.forEach((position) => {
this.featureSearchService.visualization.add({
position: position,
image: this.featureSearchService.markerGraphics(),
width: 32,
height: 32,
pixelOffset: new Cartesian2(0, -10),
eyeOffset: new Cartesian3(0, 0, -100),
color: Color.fromCssColorString(this.featureSearchService.pointColor)
});
});
}

performClustering(pixelRange: number, minimumClusterSize: number) {
// Calculate screen positions
const screenPositions = this.featureSearchService.visualizationPositions.map(pos =>
SceneTransforms.wgs84ToWindowCoordinates(this.viewer.scene, pos)
);

// Clustering logic
const clusters: { [key: string]: Cartesian3[] } = {};

screenPositions.forEach((screenPosition: Cartesian2, index: number) => {
let addedToCluster = false;

for (const clusterKey in clusters) {
const cluster = clusters[clusterKey];
const clusterScreenPosition = SceneTransforms.wgs84ToWindowCoordinates(this.viewer.scene, cluster[0]);

if (Cartesian2.distance(screenPosition, clusterScreenPosition) < pixelRange) {
cluster.push(this.featureSearchService.visualizationPositions[index]);
addedToCluster = true;
break;
}
}

if (!addedToCluster) {
clusters[index] = [this.featureSearchService.visualizationPositions[index]];
}
});

// Clear the current billboards and add clustered billboards
this.featureSearchService.visualization.removeAll();

Object.values(clusters).forEach(cluster => {
if (cluster.length >= minimumClusterSize) {
const averagePosition = this.calculateAveragePosition(cluster);
const clusterImage = this.pinBuilder?.fromText(
cluster.length.toString(),
Color.fromCssColorString(this.featureSearchService.pointColor),
48
).toDataURL();
this.featureSearchService.visualization.add({
position: averagePosition,
image: clusterImage,
width: 48,
height: 48,
eyeOffset: new Cartesian3(0, 0, -100)
});
} else {
cluster.forEach(position => {
this.featureSearchService.visualization.add({
position: position,
image: this.featureSearchService.markerGraphics(),
width: 32,
height: 32,
pixelOffset: new Cartesian2(0, -10),
eyeOffset: new Cartesian3(0, 0, -100),
color: Color.fromCssColorString(this.featureSearchService.pointColor)
});
});
}
});
}

calculateAveragePosition(cluster: Cartesian3[]): Cartesian3 {
const sum = cluster.reduce((acc, pos) => {
acc.x += pos.x;
acc.y += pos.y;
acc.z += pos.z;
return acc;
}, new Cartesian3(0, 0, 0));

return new Cartesian3(
sum.x / cluster.length,
sum.y / cluster.length,
sum.z / cluster.length
);
}

setupCameraHandlers() {
const pixelRange = 40;
const minimumClusterSize = 5;

this.viewer.scene.camera.moveEnd.addEventListener(() => {
this.performClustering(pixelRange, minimumClusterSize);
});

this.viewer.scene.camera.changed.addEventListener(() => {
this.performClustering(pixelRange, minimumClusterSize);
});

this.performClustering(pixelRange, minimumClusterSize);
}
}
5 changes: 5 additions & 0 deletions libs/core/src/bindings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,10 @@ em::val getTileIds(Viewport const& vp, int level, int limit)
return resultArray;
}

double getTilePriorityById(Viewport const& vp, uint64_t tileId) {
return Wgs84AABB::radialDistancePrioFn({vp.camPosLon, vp.camPosLat}, vp.orientation)(tileId);
}

/** Get the center position for a mapget tile id in WGS84. */
mapget::Point getTilePosition(uint64_t tileIdValue) {
mapget::TileId tid(tileIdValue);
Expand Down Expand Up @@ -321,6 +325,7 @@ EMSCRIPTEN_BINDINGS(erdblick)

////////// Viewport TileID calculation
em::function("getTileIds", &getTileIds);
em::function("getTilePriorityById", &getTilePriorityById);
em::function("getTilePosition", &getTilePosition);
em::function("getTileIdFromPosition", &getTileIdFromPosition);

Expand Down
Loading
Loading