Skip to content

Commit

Permalink
Merge pull request #213 from AuScope/AUS-3693
Browse files Browse the repository at this point in the history
AUS-3693 Suppport GeoSciML v4.1 complex boreholes with petrophysics data
  • Loading branch information
laughing0li authored Dec 2, 2022
2 parents 66c3cc7 + 8663054 commit 3a298c3
Show file tree
Hide file tree
Showing 11 changed files with 208 additions and 101 deletions.
3 changes: 2 additions & 1 deletion src/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { BsDropdownModule } from 'ngx-bootstrap/dropdown';
import { MSCLComponent } from './modalwindow/querier/customanalytic/mscl/mscl.component';
import { MSCLAnalyticComponent } from './modalwindow/layeranalytic/mscl/mscl.analytic.component';
import { MSCLService } from './modalwindow/layeranalytic/mscl/mscl.service';

import { HelpMenuComponent } from './toppanel/help-menu/help-menu.component';

Expand Down Expand Up @@ -133,7 +134,7 @@ PlotlyModule.plotlyjs = PlotlyJS;
DataExplorerComponent,
RecordModalComponent
],
providers: [ AuscopeApiService, FilterService, RectanglesEditorService, AdvancedComponentService, SearchService, NVCLService, GraceService, {provide: SAVER, useFactory: getSaver} ],
providers: [ AuscopeApiService, FilterService, RectanglesEditorService, AdvancedComponentService, SearchService, NVCLService, MSCLService, GraceService, {provide: SAVER, useFactory: getSaver} ],
imports: [
PortalCoreModule.forRoot(environment, config),
PortalCorePipesModule,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -720,7 +720,7 @@ export class DownloadPanelComponent implements OnInit {
if (UtilitiesService.isEmpty(err.message)) {
alert('An error has occurred whilst attempting to download. Please contact cg-admin@csiro.au');
} else {
alert('An error has occurred whilst attempting to download (' + err.message + '). Plese contact cg-admin@csiro.au');
alert('An error has occurred whilst attempting to download (' + err.message + '). Please contact cg-admin@csiro.au');
}
});

Expand Down
15 changes: 0 additions & 15 deletions src/app/menupanel/common/filterpanel/filterpanel.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -178,21 +178,6 @@ export class FilterPanelComponent implements OnInit {
optionalFilters: _.cloneDeep(this.optionalFilters)
};

// VT: append advance filter to mandatory filter.
/*
// deprecated, use AdvanceFilterDirective
for (const idx in this.advancedParam) {
if (!this.layer.filterCollection.mandatoryFilters) {
this.layer.filterCollection.mandatoryFilters = [];
}
this.layer.filterCollection.mandatoryFilters.push({
parameter: idx,
value: this.advancedParam[idx]
});
}
*/
// VT: End append

// TODO: Store time period with state
// WMS layers may have a time set
if (this.layerTimes.currentTime) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { LayerAnalyticInterface } from './layer.analytic.interface';
export class DynamicLayerAnalyticComponent {
private _layer: LayerModel;
@ViewChild('dynamicLayerContentAnalyticPlaceholder', { read: ViewContainerRef, static: true })
dyanmicAnalyticHost: ViewContainerRef;
dynamicAnalyticHost: ViewContainerRef;


constructor(private componentFactoryResolver: ComponentFactoryResolver, private changeDetectorRef: ChangeDetectorRef ) { }
Expand All @@ -34,7 +34,7 @@ export class DynamicLayerAnalyticComponent {

const componentFactory = this.componentFactoryResolver.resolveComponentFactory(ref.layeranalytic[this._layer.id]);

const viewContainerRef = this.dyanmicAnalyticHost
const viewContainerRef = this.dynamicAnalyticHost
viewContainerRef.clear();
const componentRef = viewContainerRef.createComponent(componentFactory);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export class MSCLAnalyticComponent implements OnInit {
featureId: string; // Identifier of the borehole
metricList: string[]; // List of metric enums to plot
closeGraphModal: () => null; // Function to call when the modal dialogue must be closed
usesGMLObs: boolean; // Response has values nested within GeoSciML observations
serviceUrl: string; // URL of MSCL service
processingData = false;

Expand Down Expand Up @@ -42,7 +43,7 @@ export class MSCLAnalyticComponent implements OnInit {
const error_display = this.error_display.nativeElement;
// Fetch data from MSCL service
this.processingData = true;
this.msclService.getMSCLDownload(this.serviceUrl, this.featureId, this.startDepth, this.endDepth, this.metricList).subscribe(valuesList => {
this.msclService.getMSCLDownload(this.serviceUrl, this.featureId, this.startDepth, this.endDepth, this.usesGMLObs, this.metricList).subscribe(valuesList => {
// Check response
if (valuesList == null || !(Symbol.iterator in Object(valuesList))) {
this.processingData = false;
Expand All @@ -60,7 +61,11 @@ export class MSCLAnalyticComponent implements OnInit {
for (const values of valuesList) {
for (const metricEnum of this.metricList) {
const featName = this.msclService.getMetricInfoAttr(metricEnum, 'feat_elem');
xLists[metricEnum].push(values[featName]);
if (this.usesGMLObs) {
xLists[metricEnum].push(values[featName.replace(/_/g, ' ')]);
} else {
xLists[metricEnum].push(values[featName]);
}
}
yList.push(values.depth);
}
Expand Down
96 changes: 92 additions & 4 deletions src/app/modalwindow/layeranalytic/mscl/mscl.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { Injectable } from '@angular/core';
import { HttpClient, HttpParams, HttpHeaders, HttpResponse } from '@angular/common/http';
import { environment } from '../../../../environments/environment';
import { Layout, Data } from 'plotly.js-dist-min';
import { SimpleXMLService } from '@auscope/portal-core-ui';

// Elements detectable via XRF
const XRFElem = ['Ti', 'V', 'Cr', 'Mn', 'Fe', 'Co', 'Ni', 'Cu', 'Zn', 'As', 'Se', 'Rb', 'Sr', 'Y', 'Zr', 'Nb', 'Mo',
Expand All @@ -20,6 +21,7 @@ export enum Metric { diameter = "diameter",
pWaveAmp = "pWaveAmp",
pWaveVel = "pWaveVel",
density = "density",
specificGravity = "specificGravity",
magSuscPoint = "magSuscPoint",
magSuscLoopVC = "magSuscLoopVC",
magSuscLoopDC = "magSuscLoopDC",
Expand Down Expand Up @@ -61,6 +63,7 @@ let metricMap: Map<string, Info> = new Map( [
[ Metric.pWaveVel, { pname: 'P-Wave Vel.', group: 'P-Wave', desc: 'P-Wave Velocity', units: 'm/s', feat_elem: 'p_wave_velocity'}],
[ Metric.pWaveAmp, { pname: 'P-Wave Amp.', group: 'P-Wave', desc: 'P-Wave Amplitude', units: '', feat_elem: 'p_wave_amplitude'}],
[ Metric.density, { pname: 'Density', group: '', desc: 'Density', units: '', feat_elem: 'density'} ],
[ Metric.specificGravity, { pname: 'Specific Gravity', group: '', desc: 'Specfic Gravity', units: '', feat_elem: 'specific_gravity'} ],
[ Metric.magSuscPoint, { pname: 'Mag. Susc. Point', group: '', desc: 'Magnetic Susceptibility Point', units: 'SI x 10^-5', feat_elem: 'magnetic_susc_point'} ],
[ Metric.magSuscLoopVC, { pname: 'Mag. Susc. LoopVC', group: '', desc: 'Magnetic Susceptibility Loop Volume Corrected', units: 'SI x 10^-5', feat_elem: 'magnetic_susceptibility'} ],
[ Metric.magSuscLoopDC, { pname: 'Mag. Susc. LoopDC', group: '', desc: 'Magnetic Susceptibility Loop Density Corrected', units: 'SI x 10^-5', feat_elem: 'magnetic_susc_loop_dc'} ],
Expand Down Expand Up @@ -134,7 +137,7 @@ export class MSCLService {
// Convert feature name list to a list of names and group names
for (let featElem of featList) {
for (let mm of metricMap.values()) {
if (mm.feat_elem === featElem) {
if (mm.feat_elem === featElem.replace(/ /g, '_')) {
if (!retList.includes(mm.pname)) {
retList.push(mm.pname);
}
Expand Down Expand Up @@ -441,6 +444,83 @@ export class MSCLService {
return traceList;
}

/**
* Checks to see if there are petrophysics observation sample values
* Assumes GeoSciML v4.1 WFS response format
*
* @param XML string WFS response
* @returns true if values could be found
*/
public usesGMLObs(xmlStr: string): boolean {
const obsIdx = xmlStr.search(/<om:OM_Observation (gml:)?id="om.observation.petrophysicalproperty/);
return (obsIdx > 0);
}

/**
* Namespace resolver for XPATH parsing of GeoSciML v4.1 WFS response
*/
private nsResolver(prefix: any) {
switch (prefix) {
case 'wfs':
return "http://www.opengis.net/wfs";
case "xs":
return "http://www.w3.org/2001/XMLSchema";
case "swe":
return "http://www.opengis.net/swe/2.0";
case "sams":
return "http://www.opengis.net/samplingSpatial/2.0";
case "gsmlp":
return "http://xmlns.geosciml.org/geosciml-portrayal/4.0";
case "gml":
return "http://www.opengis.net/gml";
case "mt":
return "http://xmlns.geoscience.gov.au/mineraltenementml/1.0";
case "cit":
return "http://standards.iso.org/iso/19115/-3/cit/1.0";
case "gco2":
return "http://www.isotc211.org/2005/gco";
case "gsmlb":
return "http://www.opengis.net/gsml/4.1/GeoSciML-Basic";
case "gsml":
return "urn:cgi:xmlns:CGI:GeoSciML:2.0";
case "gsmlbh":
return "http://www.opengis.net/gsml/4.1/Borehole";
case "erl":
return "http://xmlns.earthresourceml.org/earthresourceml-lite/2.0";
case "om":
return "http://www.opengis.net/om/2.0";
case "sam":
return "http://www.opengis.net/sampling/2.0";
case "xlink":
return "http://www.w3.org/1999/xlink";
case "gmd":
return "http://www.isotc211.org/2005/gmd";
case "xsi":
return "http://www.w3.org/2001/XMLSchema-instance";
}
return "http://www.http://www.w3.org/2001/XMLSchema-instance.net/wcs";
}

/**
* Parses an XML WFS response to find the set of metric types available
* Applies only to GeoSciML v4.1
*
* @param xmlStr WFS response
* @returns a list of strings or empty list if not found
*/
public findMetricTypes(xmlStr: string): any[] {
const rootNode = SimpleXMLService.parseStringToDOM(xmlStr);
const METRICS = '//gsmlbh:specification/om:OM_Observation/om:result/swe:Quantity/swe:label';
const nodeList = SimpleXMLService.evaluateXPathNodeArray(rootNode, rootNode, METRICS, this.nsResolver);
const metricVals = [];
for (const node of nodeList) {
metricVals.push(node.textContent);
}
const metricSet = new Set(metricVals);
const uniqueMetrics = [ ... metricSet ];
return uniqueMetrics;
}


/**
* Contacts the MSCL data service and retrieves plot data
Expand All @@ -449,10 +529,12 @@ export class MSCLService {
* @param boreholeHeaderId borehole identifier
* @param startDepth retrieve plot data starting at this depth
* @param endDepth retrieve plot data ending at this depth
* @param useGMLObs if true then observations are hidden in complete GeoSciML data model
* @param metricList list of metrics for which plotting data is required
* @return Observable for waiting on
*/
public getMSCLDownload(serviceUrl: string, boreholeHeaderId: string, startDepth: number, endDepth: number, metricList: string[]): Observable<any> {
public getMSCLDownload(serviceUrl: string, boreholeHeaderId: string, startDepth: number, endDepth: number,
useGMLObs: boolean, metricList: string[]): Observable<any> {
let httpParams = new HttpParams();
httpParams = httpParams.append('serviceUrl', serviceUrl);
httpParams = httpParams.append('boreholeHeaderId', boreholeHeaderId);
Expand All @@ -462,15 +544,21 @@ export class MSCLService {
for (const metric of metricList) {
const feat_elem = this.getMetricInfoAttr(metric, 'feat_elem');
if (feat_elem != '') {
httpParams = httpParams.append('observationsToReturn', feat_elem);
const std_feat_elem = (useGMLObs) ? feat_elem.replace('_',' '): feat_elem;
httpParams = httpParams.append('observationsToReturn', std_feat_elem);
// If user requested a group name, append all members of group
} else if (this.isMetricGroup(metric)) {
const gMetricList = this.getInfoAttrsForGrp(metric, 'feat_elem');
for (let gMet of gMetricList) {
httpParams = httpParams.append('observationsToReturn', gMet);
const cleanMetric = (useGMLObs) ? gMet.replace('_',' '): gMet;
httpParams = httpParams.append('observationsToReturn', cleanMetric);
}
}
}
// Observations are hidden in complete GeoSciML data model
if (useGMLObs) {
httpParams = httpParams.append('useGMLObs', 'true');
}
// Send HTTP request for observations for a borehole
return this.http.post(environment.portalBaseUrl + 'getMsclObservationsForGraph.do', httpParams.toString(), {
headers: new HttpHeaders().set('Content-Type', 'application/x-www-form-urlencoded'),
Expand Down
20 changes: 16 additions & 4 deletions src/app/modalwindow/querier/customanalytic/mscl/mscl.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export class MSCLComponent implements OnInit {
public allTicked = false; // Are all tickboxes ticked?
public showSelectMetricError: boolean; // Show error if no metrics chosen when Draw Graph is pressed

private usesGMLObs = false; // Response has values nested within GeoSciML observations
private bsModalRef: BsModalRef;

constructor(public msclService: MSCLService, private modalService: BsModalService, private changeDetectorRef: ChangeDetectorRef) {
Expand All @@ -47,9 +48,19 @@ export class MSCLComponent implements OnInit {
// Extract the available metrics from the "datasetProperties" XML element in the WFS response
// "datasetProperties" is a list of the metrics available in this borehole's dataset
// The members of the list take the form of XML element names e.g. p_wave_velocity
const metrics = /<gsmlp:datasetProperties>[a-z_,]*<\/gsmlp:datasetProperties>/.exec(this.doc.raw).toString();
// Remove tags at ends and convert to a list
const metricList = metrics.substring(25, metrics.length - 26).split(",");
let metricList = [];
// Find out if values are nested within GeoSciML observations
this.usesGMLObs = this.msclService.usesGMLObs(this.doc.raw);
if (this.usesGMLObs) {
metricList = this.msclService.findMetricTypes(this.doc.raw);
} else {
const searchResult = /<gsmlp:datasetProperties>[a-z_,]*<\/gsmlp:datasetProperties>/.exec(this.doc.raw);
if (searchResult) {
const metricsStr = searchResult.toString();
// Remove tags at ends and convert to a list
metricList = metricsStr.substring(25, metricsStr.length - 26).split(",");
}
}
this.metricPNameList = this.msclService.getMetricPNameList(metricList)

// Given list of metrics, set up the data structures that support tickboxes
Expand Down Expand Up @@ -115,7 +126,8 @@ export class MSCLComponent implements OnInit {
'metricList': selecMetricList,
'featureId': this.featureId,
'closeGraphModal': this.closeGraphModal.bind(this),
'serviceUrl': this.onlineResource.url
'serviceUrl': this.onlineResource.url,
'usesGMLObs': this.usesGMLObs
}
});
this.modalDisplayed = true;
Expand Down
15 changes: 7 additions & 8 deletions src/app/modalwindow/querier/dynamic.analytic.component.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { LayerModel } from '@auscope/portal-core-ui';
import { OnlineResourceModel } from '@auscope/portal-core-ui';
import { NVCLDatasetListComponent } from './customanalytic/nvcl/nvcl.datasetlist.component';
import { Component, Input, ViewChild, ComponentFactoryResolver, ViewContainerRef, ChangeDetectorRef } from '@angular/core';
import { Component, Input, ViewChild, ViewContainerRef, ChangeDetectorRef } from '@angular/core';
import {ref} from '../../../environments/ref';
import { QuerierInfoModel } from '@auscope/portal-core-ui';
import { RemanentAnomaliesComponent } from './customanalytic/RemanentAnomalies/remanentanomalies.component';
Expand All @@ -21,10 +21,10 @@ export class DynamicAnalyticComponent {
@Input() doc: QuerierInfoModel;
private _load: boolean;
@ViewChild('dynamicContentAnalyticPlaceholder', { read: ViewContainerRef, static: true })
dyanmicAnalyticHost: ViewContainerRef;
dynamicAnalyticHost: ViewContainerRef;


constructor(private componentFactoryResolver: ComponentFactoryResolver, private changeDetectorRef: ChangeDetectorRef ) { }
constructor(private changeDetectorRef: ChangeDetectorRef) { }

@Input()
set load(load: boolean) {
Expand All @@ -36,12 +36,11 @@ export class DynamicAnalyticComponent {

loadComponent() {


const componentFactory = this.componentFactoryResolver.resolveComponentFactory(ref.analytic[this.layer.id]);

const viewContainerRef = this.dyanmicAnalyticHost
const viewContainerRef = this.dynamicAnalyticHost
viewContainerRef.clear();
const componentRef = viewContainerRef.createComponent(componentFactory);
// Default to MSCLComponent
const component = this.layer.id in ref.analytic? ref.analytic[this.layer.id]: MSCLComponent;
const componentRef = viewContainerRef.createComponent(component);

(<NVCLDatasetListComponent>componentRef.instance).layer = this.layer;
(<NVCLDatasetListComponent>componentRef.instance).onlineResource = this.onlineResource;
Expand Down
Loading

0 comments on commit 3a298c3

Please sign in to comment.