diff --git a/src/components/CoordResolverInput.vue b/src/components/CoordResolverInput.vue
new file mode 100644
index 0000000..eae2312
--- /dev/null
+++ b/src/components/CoordResolverInput.vue
@@ -0,0 +1,65 @@
+
+
+
+
+
+ Resolve
+
+
+
+
+ Simbad
+
+
+
+
+ NED
+
+
+
+
+ {{ searchStore.resolverMessage }}
+
+
+
+
\ No newline at end of file
diff --git a/src/store/app.ts b/src/store/app.ts
index 8174f43..e199547 100644
--- a/src/store/app.ts
+++ b/src/store/app.ts
@@ -1,5 +1,7 @@
// Utilities
import { defineStore } from 'pinia'
+import axios, { AxiosResponse } from 'axios'
+import {parseSimbadResponse, parseNEDResponse} from '@/utils/parseSimbadNEDResponse'
export const useAppStore = defineStore('app', {
state: () => ({
@@ -77,3 +79,84 @@ export const useAppStore = defineStore('app', {
}
})
+
+
+// Split store in logical component. `search` store will contain all data related to search form/process.
+export const useSearchStore = defineStore('search', {
+ state: () => ({
+ resolverName: '',
+ resolverServer: 'simbad',
+ resolverStatus: null,
+ resolverMessage: '',
+ resolverCoords: null,
+ resolverIsLoading: false,
+ }),
+ actions: {
+ resolveName() {
+
+ let url: string
+
+ // Resolve button clicked without providing the target name
+ if (this.resolverName == '') {
+ this.resolverStatus = 'error'
+ this.resolverMessage = 'Target Name is required'
+ this.resolverCoords = null
+ this.resolverIsLoading = false
+ return
+ }
+
+ // select server resolver
+ switch (this.resolverServer) {
+ case 'simbad':
+ url = `https://simbad.cds.unistra.fr/simbad/sim-id?Ident=${encodeURIComponent(this.resolverName)}&output.format=votable&output.params=main_id,coo(d)`
+ break
+ case 'ned':
+ // NED via Sesame CDS https://vizier.cds.unistra.fr/vizier/doc/sesame.htx
+ // NED directly does not allow due to CORS
+ url = `https://cds.unistra.fr/cgi-bin/nph-sesame/-ox/~N?${encodeURIComponent(this.resolverName)}`
+ break
+ default:
+ throw new Error('Unknown name resolver')
+ }
+
+ // set loading status while response is going on
+ this.resolverIsLoading = true
+
+ let res: {status: string, message: string, payload: any}
+
+ axios
+ .get(url)
+ .then((response) => {
+ switch (this.resolverServer) {
+ case 'simbad':
+ res = parseSimbadResponse(response.data)
+ break
+ case 'ned':
+ res = parseNEDResponse(response.data)
+ break
+ }
+ // successful server response, but status can be `error` if cannot find object in DB
+ this.resolverStatus = res.status
+ this.resolverMessage = res.message
+ this.resolverCoords = res.payload
+ this.resolverIsLoading = false
+ })
+ .catch((error) => {
+ // error response is something went wrong
+ this.resolverStatus = 'error'
+ this.resolverMessage = `"${this.resolverServer}" cannot successfully resolve "${this.resolverName}" to coordinates`
+ this.resolverCoords = null
+ this.resolverIsLoading = false
+ // error details will be posted in console
+ console.warn(error);
+ })
+ },
+ // cleanup Target Resolver input
+ resetResolver() {
+ this.resolverStatus = null,
+ this.resolverMessage = '',
+ this.resolverCoords = null,
+ this.resolverIsLoading = false
+ }
+ }
+})
\ No newline at end of file
diff --git a/src/utils/parseSimbadNEDResponse.ts b/src/utils/parseSimbadNEDResponse.ts
new file mode 100644
index 0000000..5a0ced7
--- /dev/null
+++ b/src/utils/parseSimbadNEDResponse.ts
@@ -0,0 +1,76 @@
+interface ResponsePayload {
+ status?: string
+ message: string
+ payload: any
+}
+
+
+export function parseSimbadResponse(data: any): ResponsePayload {
+
+ // Parse the XML string into an XMLDocument
+ const parser = new DOMParser()
+ const xmlDoc = parser.parseFromString(data, 'text/xml')
+
+ // Find the INFO element within the XML document suggesting a resolution error
+ const infoElement = xmlDoc.querySelector('INFO')
+
+ if (infoElement) {
+ // Get error information from the INFO element
+ const errorInfo = infoElement.getAttribute('value')
+ console.warn(errorInfo)
+ return { status: 'error', message: `Simbad: ${errorInfo}`, payload: null }
+
+ } else {
+
+ // Find the relevant elements within the XML document
+ const tableData = xmlDoc.querySelector('TABLEDATA')
+ const rowData = tableData.querySelector('TR')
+ const columns = rowData.querySelectorAll('TD')
+
+ // Extract the data from the columns
+ const name: string = columns[0].textContent?.trim() || '' // Main identifier
+ const RA_d: number = parseFloat(columns[1].textContent || '') // Right ascension
+ const DEC_d: number = parseFloat(columns[2].textContent || '') // Declination
+
+ // Construct the output object
+ const output: { name: string, ra: number, dec: number } = { name: name, ra: RA_d, dec: DEC_d }
+
+ return {status: 'success', message: 'Resolved successfully', payload: output }
+ }
+}
+
+
+export function parseNEDResponse(data: any): ResponsePayload {
+
+ // Parse the XML string into an XMLDocument
+ const parser = new DOMParser()
+ const xmlDoc = parser.parseFromString(data, 'text/xml')
+
+ // Get the Resolver element
+ const resolverElement = xmlDoc.querySelector('Resolver');
+
+ // Find the INFO element within the XML document suggesting a resolution error
+ const infoElement = xmlDoc.querySelector('INFO')
+
+ if (infoElement) {
+ // Get error information from the INFO element
+ const errorInfo = infoElement.textContent
+ console.warn(errorInfo)
+ return { status: 'error', message: `NED: ${errorInfo}`, payload: null }
+
+ } else {
+ // Extract the desired fields
+ const jra = resolverElement.querySelector('jradeg')
+ const jde = resolverElement.querySelector('jdedeg')
+
+ const name: string = resolverElement.querySelector('oname').textContent
+ const jradeg: number = parseFloat(jra.textContent || '')
+ const jdedeg: number = parseFloat(jde.textContent || '')
+
+ // Construct the output object
+ const output: { name: string, ra: number, dec: number } = { name: name, ra: jradeg, dec: jdedeg }
+
+ return { status: 'success', message: 'Resolved successfully', payload: output }
+ }
+
+}
diff --git a/src/views/Search.vue b/src/views/Search.vue
index d6acc8c..55101ca 100644
--- a/src/views/Search.vue
+++ b/src/views/Search.vue
@@ -1,9 +1,14 @@
+
+
+
+
+
-
+
-
+
-
+
-
+
-
-
+
+
+
-
-
+
+
+
+
+
+
+
+
+
+
+ BHM
+ MWM
+ LWM
+
@@ -84,11 +111,14 @@ import axios from 'axios'
import { ref, onMounted, watch } from 'vue'
import TextInput from '@/components/TextInput.vue'
import DropdownSelect from '@/components/DropdownSelect.vue';
+import CoordResolverInput from '@/components/CoordResolverInput.vue';
import { useRouter } from 'vue-router';
-import { useAppStore } from '@/store/app'
+import { useAppStore, useSearchStore } from '@/store/app'
+import { storeToRefs } from 'pinia'
// get the application state store and router
const store = useAppStore()
+const searchStore = useSearchStore()
const router = useRouter()
// set up a form reference, is the name in v-form ref="form"
@@ -124,12 +154,14 @@ let initFormData = {
coords: '',
ra: '',
dec: '',
- radius: '',
+ radius: '0.01',
id: '',
units: 'degree',
release: store.release,
carton: '',
- program: ''
+ program: '',
+ mapper: ['bhm', 'mwm', 'lwm'],
+ fileTargets: [],
}
// create dynamic bindings to form fields
let formData = ref({ ...initFormData })
@@ -145,6 +177,13 @@ watch(formData, async () => {
valid.value = formValid.valid; // update the valid state
}, { deep: true }); // deep watch to track nested property changes
+// watcher to update coordinate input if name has been successfully resolved to coordinates
+const { resolverCoords } = storeToRefs(searchStore)
+watch(resolverCoords, () => {
+ if (resolverCoords.value) {
+ formData.value.coords = `${resolverCoords.value.ra}, ${resolverCoords.value.dec}`
+ }
+})
// events
async function submit_form(this: any) {