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

Research app selection messaging #133

Merged
merged 11 commits into from
Aug 23, 2021
Merged
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
2 changes: 2 additions & 0 deletions research-app-messages/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@

import * as classicPywwt from './classic_pywwt';
import * as layers from './layers';
import * as selections from './selections';
import * as settings from './settings';

export {
classicPywwt,
layers,
selections,
settings,
};

Expand Down
46 changes: 46 additions & 0 deletions research-app-messages/src/selections.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/**
* Information about a selected source in the engine.
* The values for right ascension and declination are given in radians.
* `catalogName` gives the name of the associated HiPS catalog,
* while `name` gives a string name for the source.
*/
export interface Source {
ra: number;
dec: number;
name: string;
catalogName: string;
zoomDeg?: number;
catalogData: {
[field: string]: string | undefined;
};
}

/** Information about the current state of source and catalog selection
* inside the WWT application
*
* This message is broadcasted by the application whenever one of the user's selection
* options inside of the application is adjusted. The Vue listeners for the selected
* catalogs and sources are deep listeners, meaning that this message will be broadcasted
* whenever a property of one of their items changes.
*
* Not all fields of this message will always be present, depending on the
* nature of the event triggering the emission of this message. A message
* missing a particular field should be treated as conveying no information
* about the state described by that field.
*/
export interface SelectionStateMessage {

/** The tag identifying this message type. */
type: "wwt_selection_state";

/** An app/client session identifier. See the description for [[ViewStateMessage.sessionId]].
*/
sessionId: string;

/** The most recent source that was added to the selection list. */
mostRecentSource?: Source;

/** The list of sources that are currently selected. */
selectedSources?: Source[];
}

104 changes: 73 additions & 31 deletions research-app/src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,7 @@ import {
classicPywwt,
isPingPongMessage,
layers,
selections,
settings,
ApplicationStateMessage,
ViewStateMessage,
Expand Down Expand Up @@ -841,6 +842,14 @@ interface AngleCoordinates {
dec: number;
}

interface RawSourceInfo {
ra: number;
dec: number;
catalogName: string;
colNames: string[];
values: string[];
}

/** Get the source of a MessageEvent as a Window, if it is one.
*
* The problem here is that on Chrome, if the event is a cross-origin message
Expand Down Expand Up @@ -875,7 +884,8 @@ export default class App extends WWTAwareComponent {

defaultColor = Color.fromArgb(1, 255, 255, 255);
wwtComponentNamespace = wwtEngineNamespace;
lastClosePt: Source | null = null;
lastClosePt: RawSourceInfo | null = null;
lastSelectedSource: Source | null = null;
distanceThreshold = 0.01;
hideAllChrome = false;
hipsUrl = "http://www.worldwidetelescope.org/wwtweb/catalog.aspx?W=hips"; // Temporary
Expand Down Expand Up @@ -1302,16 +1312,13 @@ export default class App extends WWTAwareComponent {

wwtOnMouseUp(_event: MouseEvent) {
if (!this.isMouseMoving && this.lastClosePt !== null) {
const source: Source = {
...this.lastClosePt,
name: this.nameForSource(this.lastClosePt),
};
const source = this.sourceCreator(this.lastClosePt);
this.addSource(source);
this.lastSelectedSource = source;
}
this.isMouseMoving = false;
}

// Increment the counter by 1 every time this is called

newSourceName = (function () {
let count = 0;

Expand All @@ -1321,15 +1328,29 @@ export default class App extends WWTAwareComponent {
};
})();

nameForSource(source: any): string {
nameForSource(catalogData: any, catalogName: string): string {
for (const [key, [from, to]] of Object.entries(this.catalogNameMappings)) {
if (from in source && source["catalogName"] === key) {
return `${to}: ${source[from]}`;
if (from in catalogData && catalogName === key) {
return `${to}: ${catalogData[from]}`;
}
}
return this.newSourceName();
}

sourceCreator(sourceInfo: RawSourceInfo): Source {
const obj: any = {};
for (let i = 0; i < sourceInfo.values.length; i++) {
obj[sourceInfo.colNames[i]] = sourceInfo.values[i];
}
return {
ra: sourceInfo.ra,
dec: sourceInfo.dec,
catalogName: sourceInfo.catalogName,
catalogData: obj,
name: this.nameForSource(obj, sourceInfo.catalogName),
};
}

// ImageSet layers, including FITS layers:

// These maps are keyed by "external" layer IDs
Expand Down Expand Up @@ -1755,6 +1776,40 @@ export default class App extends WWTAwareComponent {
this.statusMessageDestination.postMessage(msg, this.allowedOrigin);
}

@Watch("lastSelectedSource")
onLastSelectedSourceChanged(source: Source) {
// Notify clients when a source is selected

if (this.statusMessageDestination === null || this.allowedOrigin === null)
return;

const msg: selections.SelectionStateMessage = {
type: "wwt_selection_state",
sessionId: this.statusMessageSessionId,
mostRecentSource: source,
};

this.statusMessageDestination.postMessage(msg, this.allowedOrigin);
}

@Watch("sources", {deep:true})
onSelectedSourcesChanged(sources: Source[]) {
// Notify clients when the list of selected sources is changed
// By making this a deep watcher, it keeps of track of any change
// in the list - even events like a property of a list entry changing

if (this.statusMessageDestination === null || this.allowedOrigin === null)
return;

const msg: selections.SelectionStateMessage = {
type: "wwt_selection_state",
sessionId: this.statusMessageSessionId,
selectedSources: sources,
};

this.statusMessageDestination.postMessage(msg, this.allowedOrigin);
}

// A client has requested that we load a HiPS catalog. Once it's loaded we
// reply to the client with the details of the catalog-as-spreadsheet-layer,
// so that it can know what the catalog's characteristics are.
Expand Down Expand Up @@ -1936,16 +1991,16 @@ export default class App extends WWTAwareComponent {
);
}

closestInView(target: AngleCoordinates, threshold?: number): Source | null {
closestInView(target: AngleCoordinates, threshold?: number): RawSourceInfo | null {
let minDist = Infinity;
let closestPt = null;

const rowSeparator = "\r\n";
const colSeparator = "\t";

for (const catalog of this.visibleHipsCatalogs()) {
const name = catalog.name;
const layer = this.layerForHipsCatalog(name);
const catalogName = catalog.name;
const layer = this.layerForHipsCatalog(catalogName);
if (layer == null) {
continue;
}
Expand All @@ -1960,27 +2015,14 @@ export default class App extends WWTAwareComponent {
const lngCol = layer.get_lngColumn();
const latCol = layer.get_latColumn();

const itemCreator = function (values: string[]): Source {
const obj: any = {};
for (let i = 0; i < values.length; i++) {
obj[colNames[i]] = values[i];
}
return {
...obj,
ra: D2R * Number(values[lngCol]),
dec: D2R * Number(values[latCol]),
catalogName: name,
};
};

for (const row of rows) {
const items = row.split(colSeparator);
const ra = Number(items[lngCol]);
const dec = Number(items[latCol]);
const pt = { ra: D2R * ra, dec: D2R * dec };
const values = row.split(colSeparator);
const ra = D2R * Number(values[lngCol]);
const dec = D2R * Number(values[latCol]);
const pt = { ra: ra, dec: dec };
const dist = distance(target.ra, target.dec, pt.ra, pt.dec);
if (dist < minDist) {
closestPt = itemCreator(items);
closestPt = { ra: ra, dec: dec, colNames: colNames, values: values, catalogName: catalogName };
minDist = dist;
}
}
Expand Down
5 changes: 4 additions & 1 deletion research-app/src/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ export interface Source {
name: string;
catalogName: string;
zoomDeg?: number;
catalogData: {
[field: string]: string | undefined;
};
}

export interface HipsCatalogStatus {
Expand Down Expand Up @@ -45,7 +48,7 @@ function removeFromArray<T>(array: T[], item: T, equivalent: EquivalenceTest<T>
}

function sourcesEqual(s1: Source, s2: Source) {
return (s1.ra === s2.ra) && (s1.dec === s2.dec) && (s1.name === s2.name) && (s1.catalogName === s2.catalogName);
return (s1.ra === s2.ra) && (s1.dec === s2.dec) && (s1.catalogName === s2.catalogName);
}

@Module({
Expand Down