Skip to content

Commit

Permalink
Merge dev into master (#324)
Browse files Browse the repository at this point in the history
* Feature/1929 - Values-scale and contours adaption in the frontend

* Use 1% quantile when generating legend

* Bugfix, error in packages recalculation with older object-structure

* Feature/1954 - T10: Implement slider for (visual) selection of time ranges in Time processing

* Add options for cutting out time periods

* Feature/1954 - T10: Implement slider for (visual) selection of time ranges in Time processing

* Remove console.log

* Feature/1954 - T10: Implement slider for (visual) selection of time ranges in Time processing

* Fix value processing: value, begin, end input fields are now working properly
* Add time processing information to processing table

* Feature/1954 - T10: Implement slider for (visual) selection of time ranges in Time processing

* Fix error when values are NULL in processed data
* Resolves #1971

* Feature/1917 budget results (#321)

* Feature/1946 - Add points to results charts

* Add min, max and turning-points
* Fix saving meta data

* Feature/1917 - Budget results

* Implement new results
* Show raster data as lines instead of polygons

* Feature/1917 - Budget Results: Slider not working, when nstp > 1

* Limit number of results for high nstp values (> 50 ... can be adjusted in ocPackageProperties.tsx)

* Feature/1978 - Allow all geometries for boundaries

* Implement point to lineString conversion

* Validate schema before sending to calculation server

* Feature/1978 - Allow all geometries for boundaries

* Implement point to polygon conversion
* Implement line to polygon conversion

Co-authored-by: Robert Schlick <[email protected]>
Co-authored-by: Robert <[email protected]>
  • Loading branch information
3 people authored Nov 4, 2020
1 parent 603a8d7 commit 75aea9f
Show file tree
Hide file tree
Showing 34 changed files with 707 additions and 408 deletions.
13 changes: 6 additions & 7 deletions src/core/model/flopy/packages/mf/FlopyModflow.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import {GenericObject} from '../../../genericObject/GenericObject';
import {IFlopyModflow} from './FlopyModflow.type';
import {IPropertyValueObject} from '../../../types';
import {ModflowModel} from '../../../modflow';
import BoundaryCollection from '../../../modflow/boundaries/BoundaryCollection';
import Soilmodel from '../../../modflow/soilmodel/Soilmodel';
import {IPropertyValueObject} from '../../../types';
import {IFlopyModflow} from './FlopyModflow.type';
import FlopyModflowFlowPackage from './FlopyModflowFlowPackage';
import FlopyModflowMf from './FlopyModflowMf';
import FlopyModflowMfbas from './FlopyModflowMfbas';
Expand Down Expand Up @@ -36,6 +35,7 @@ import FlopyModflowMfupw from './FlopyModflowMfupw';
import FlopyModflowMfwel from './FlopyModflowMfwel';
import FlopyModflowPackage from './FlopyModflowPackage';
import FlopyModflowSolverPackage from './FlopyModflowSolverPackage';
import Soilmodel from '../../../modflow/soilmodel/Soilmodel';

export const packagesMap: IPropertyValueObject = {
mf: FlopyModflowMf,
Expand Down Expand Up @@ -99,7 +99,7 @@ export default class FlopyModflow extends GenericObject<IFlopyModflow> {
pcg: FlopyModflowMfpcg.create().toObject(),

// Output control
oc: FlopyModflowMfoc.create(model.stressperiods.count).toObject()
oc: FlopyModflowMfoc.create(model).toObject()
};

// Boundaries
Expand Down Expand Up @@ -297,9 +297,8 @@ export default class FlopyModflow extends GenericObject<IFlopyModflow> {
case 'oc':
let oc;
this._props.oc ?
oc = FlopyModflowMfoc.fromObject(this._props.oc).update(model.stressperiods.stressperiods.length,
this._props.oc.stress_period_data) :
oc = FlopyModflowMfoc.create(model.stressperiods.stressperiods.length);
oc = FlopyModflowMfoc.fromObject(this._props.oc).update(model, this._props.oc.stress_period_data) :
oc = FlopyModflowMfoc.create(model);
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
oc ? this._props.oc = oc.toObject() : delete this._props.oc;
Expand Down
30 changes: 24 additions & 6 deletions src/core/model/flopy/packages/mf/FlopyModflowMfoc.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {IPropertyValueObject} from '../../../types';
import {ModflowModel} from '../../../modflow';
import FlopyModflowPackage from './FlopyModflowPackage';

export interface IFlopyModflowMfoc extends IPropertyValueObject {
Expand Down Expand Up @@ -29,10 +30,12 @@ export const defaults: IFlopyModflowMfoc = {
label: 'LABEL',
};

export const MAX_OUTPUT_PER_PERIOD = 50;

export default class FlopyModflowMfoc extends FlopyModflowPackage<IFlopyModflowMfoc> {

public static create(nper: number) {
return this.fromDefault().update(nper);
public static create(model: ModflowModel) {
return this.fromDefault().update(model);
}

public static fromDefault() {
Expand All @@ -50,13 +53,28 @@ export default class FlopyModflowMfoc extends FlopyModflowPackage<IFlopyModflowM
return new this(d);
}

public update(nper: number, data?: Array<[[number, number], string[]]>) {
public update(model: ModflowModel, data?: Array<[[number, number], string[]]>) {
const spData: IFlopyModflowMfoc['stress_period_data'] = [];
const nper = model.stressperiods.stressperiods.length;

for (let per = 0; per < nper; per++) {
if (data && data[per]) {
spData.push(data[per]);
const nstp = model.stressperiods.stressperiods[per].nstp;
if (nstp > MAX_OUTPUT_PER_PERIOD) {
const d = data ? data.filter((r) => r[0][0] === per && r[0][1] === 0) : null;
if (d && d.length > 0) {
spData.push(d[0]);
} else {
spData.push([[per, 0], ['save head', 'save drawdown', 'save budget']]);
}
} else {
spData.push([[per, 0], ['save head', 'save drawdown', 'save budget']]);
for (let tp = 0; tp < nstp; tp++) {
const d = data ? data.filter((r) => r[0][0] === per && r[0][1] === tp) : null;
if (d && d.length > 0) {
spData.push(d[0]);
} else {
spData.push([[per, tp], ['save head', 'save drawdown', 'save budget']]);
}
}
}
}

Expand Down
3 changes: 2 additions & 1 deletion src/core/model/flopy/packages/mt/FlopyMt3dMtrct.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,9 @@ class FlopyMt3dMtrct extends FlopyMt3dPackage<IFlopyMt3dMtrct> {
}

public static fromObject(obj: IPropertyValueObject): FlopyMt3dMtrct {
const d: any = FlopyMt3dPackage.cloneDeep(defaults);
const d: IFlopyMt3dMtrct = FlopyMt3dPackage.cloneDeep(defaults);
for (const key in d) {
// eslint-disable-next-line no-prototype-builtins
if (d.hasOwnProperty(key) && obj.hasOwnProperty(key)) {
d[key] = obj[key];
}
Expand Down
20 changes: 10 additions & 10 deletions src/core/model/geometry/Cells.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import * as turf from '@turf/helpers';
import {Array2D} from './Array2D.type';
import {BoundingBox, Geometry, GridSize} from '../modflow/index';
import {Feature, LineString} from 'geojson';
import {ICell, ICells, Point} from './Cells.type';
import {LineBoundary} from '../modflow/boundaries';
import {NearestPointOnLine} from '@turf/nearest-point-on-line';
import {
booleanContains,
Expand All @@ -9,12 +14,7 @@ import {
lineSlice,
nearestPointOnLine
} from '@turf/turf';
import {Feature, LineString} from 'geojson';
import {cloneDeep, floor, isEqual, uniqWith} from 'lodash';
import {LineBoundary} from '../modflow/boundaries';
import {BoundingBox, Geometry, GridSize} from '../modflow/index';
import {Array2D} from './Array2D.type';
import {ICell, ICells, Point} from './Cells.type';

const getActiveCellFromCoordinate = (coordinate: Point, boundingBox: BoundingBox, gridSize: GridSize): ICell => {

Expand Down Expand Up @@ -203,7 +203,7 @@ export default class Cells {
this._cells = cellObjs.map((li) => ([li.x, li.y, li.value]) as ICell);
};

public toggle = ([x, y]: number[], boundingBox: BoundingBox, gridSize: GridSize, transform: boolean = true) => {
public toggle = ([x, y]: number[], boundingBox: BoundingBox, gridSize: GridSize, transform = true) => {
const dx = boundingBox.dX / gridSize.nX;
const dy = boundingBox.dY / gridSize.nY;

Expand Down Expand Up @@ -239,9 +239,9 @@ export default class Cells {

public calculateIBound = (nrow: number, ncol: number) => {
const iBound2D: Array2D<number> = [];
for (let row: number = 0; row < nrow; row++) {
for (let row = 0; row < nrow; row++) {
iBound2D[row] = [0];
for (let col: number = 0; col < ncol; col++) {
for (let col = 0; col < ncol; col++) {
iBound2D[row][col] = 0;
}
}
Expand All @@ -259,8 +259,8 @@ export default class Cells {
const cells = new Cells([]);
const iBound = this.calculateIBound(gridSize.nY, gridSize.nX);

for (let rIdx: number = 0; rIdx < gridSize.nY; rIdx++) {
for (let cIdx: number = 0; cIdx < gridSize.nX; cIdx++) {
for (let rIdx = 0; rIdx < gridSize.nY; rIdx++) {
for (let cIdx = 0; cIdx < gridSize.nX; cIdx++) {
if (iBound[rIdx][cIdx] === 0) {
cells.addCell([cIdx, rIdx, 0]);
}
Expand Down
6 changes: 3 additions & 3 deletions src/core/model/geometry/Geometry.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import {AllGeoJSON, Feature} from '@turf/helpers';
import {bbox} from '@turf/turf';
import * as turf from '@turf/turf';
import {AllGeoJSON, Feature} from '@turf/helpers';
import {GeoJson} from './Geometry.type';
import {Point} from 'geojson';
import {bbox} from '@turf/turf';
import {cloneDeep} from 'lodash';
import md5 from 'md5';
import {GeoJson} from './Geometry.type';

class Geometry {

Expand Down
2 changes: 1 addition & 1 deletion src/core/model/geometry/Geometry.type.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {LineString, MultiPolygon, Point, Polygon} from 'geojson';
import {Geometry} from './index';
import {LineString, MultiPolygon, Point, Polygon} from 'geojson';

export type GeoJson = Point | LineString | Polygon | MultiPolygon;
export type IGeometry = GeoJson;
Expand Down
15 changes: 13 additions & 2 deletions src/core/model/modflow/Calculation.type.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
import {ITimeUnit} from './TimeUnit.type';

export interface ICalculationParameter {
idx: number[];
total_times: number[];
kstpkper: Array<[number, number]>;
layers?: number;
}

export interface ICalculation {
calculation_id: string;
state: number;
Expand All @@ -8,7 +15,11 @@ export interface ICalculation {
times: null | {
start_date_time: string;
time_unit: ITimeUnit;
total_times: number[]
};
total_times: number[];
head: ICalculationParameter;
budget: ICalculationParameter;
concentration: ICalculationParameter;
drawdown: ICalculationParameter;
}
layer_values: string[][];
}
9 changes: 9 additions & 0 deletions src/core/model/rtm/processing/Processing.type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,13 @@ export interface IProcessing {

export type IValueProcessingOperator = '+' | '-' | '*' | '/' | '<' | '<=' | '>' | '>=' | '=';

export enum ECutRule {
NONE = 'none',
PERIOD = 'period',
UNTIL_TODAY = 'untilToday',
BEFORE_TODAY = 'beforeToday'
}

export interface IValueProcessing extends IProcessing {
type: 'value';
operator: IValueProcessingOperator;
Expand All @@ -17,4 +24,6 @@ export interface ITimeProcessing extends IProcessing {
type: 'time';
rule: string;
method: string;
cut: ECutRule;
cutNumber?: number;
}
46 changes: 38 additions & 8 deletions src/core/model/rtm/processing/TimeProcessing.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import _ from 'lodash';
import {makeTimeProcessingRequest} from '../../../../services/api';
import {ECutRule, ITimeProcessing} from './Processing.type';
import {GenericObject} from '../../genericObject/GenericObject';
import {IDateTimeValue} from '../Sensor.type';
import {ITimeProcessing} from './Processing.type';
import {makeTimeProcessingRequest} from '../../../../services/api';
import _ from 'lodash';

export const methods = [
[
Expand Down Expand Up @@ -61,6 +61,22 @@ class TimeProcessing extends GenericObject<ITimeProcessing> {
this._props.end = value;
}

get cut(): ECutRule {
return this._props.cut;
}

set cut(value: ECutRule) {
this._props.cut = value;
}

get cutNumber(): number | undefined {
return this._props.cutNumber;
}

set cutNumber(value: number | undefined) {
this._props.cutNumber = value;
}

get type(): string {
return this._props.type;
}
Expand All @@ -82,14 +98,28 @@ class TimeProcessing extends GenericObject<ITimeProcessing> {
}

public async apply(input: IDateTimeValue[]) {
const dataToProcess = _.uniqBy(
input.filter((i) => i.timeStamp >= this.begin && i.timeStamp <= this.end), 'timeStamp');
let dataToProcess: IDateTimeValue[] = _.uniqBy(input, 'timeStamp');
if (!this.cut || this.cut === ECutRule.NONE || this.cut === ECutRule.PERIOD) {
dataToProcess = dataToProcess.filter((i) => i.timeStamp >= this.begin && i.timeStamp <= this.end);
}
if (this.cut === ECutRule.UNTIL_TODAY) {
dataToProcess = dataToProcess.filter((i) => i.timeStamp >= this.begin);
}

// eslint-disable-next-line no-useless-catch
try {
const processedData = await makeTimeProcessingRequest(dataToProcess, this.rule, this.method);
return input.filter((i) => !(i.timeStamp >= this.begin && i.timeStamp <= this.end))
.concat(processedData)
.sort((a: IDateTimeValue, b: IDateTimeValue) => a.timeStamp - b.timeStamp);

if (!this.cut || this.cut === ECutRule.NONE) {
return input.filter((i) => !(i.timeStamp >= this.begin && i.timeStamp <= this.end))
.concat(processedData)
.sort((a: IDateTimeValue, b: IDateTimeValue) => a.timeStamp - b.timeStamp);
}
if (this.cut === ECutRule.BEFORE_TODAY) {
const n = this.cutNumber || 0;
return processedData.sort((a: IDateTimeValue, b: IDateTimeValue) => a.timeStamp - b.timeStamp).slice(-1 * n);
}
return processedData.sort((a: IDateTimeValue, b: IDateTimeValue) => a.timeStamp - b.timeStamp);
} catch (e) {
throw e;
}
Expand Down
59 changes: 30 additions & 29 deletions src/scenes/shared/complexTools/ResultsMap.tsx
Original file line number Diff line number Diff line change
@@ -1,30 +1,26 @@
import {LeafletMouseEvent} from 'leaflet';
import React, {useEffect, useRef, useState} from 'react';
import {Array2D} from '../../../core/model/geometry/Array2D.type';
import {BasicTileLayer} from '../../../services/geoTools/tileLayers';
import {BoundaryCollection} from '../../../core/model/modflow/boundaries';
import {ColorLegend, ReactLeafletHeatMapCanvasOverlay} from '../rasterData';
import {
FeatureGroup, GeoJSON,
LayersControl,
Map,
Viewport
} from 'react-leaflet';
import uuid from 'uuid';
import {Array2D} from '../../../core/model/geometry/Array2D.type';
import {ICell} from '../../../core/model/geometry/Cells.type';
import {FullscreenControl} from './index';
import {Geometry, ModflowModel} from '../../../core/model/modflow';
import {BoundaryCollection} from '../../../core/model/modflow/boundaries';
import {ICell} from '../../../core/model/geometry/Cells.type';
import {IReactLeafletHeatMapProps} from '../rasterData/ReactLeafletHeatMapCanvasOverlay.type';
import {LeafletMouseEvent} from 'leaflet';
import {createGridData, rainbowFactory} from '../rasterData/helpers';
import {getCellFromClick} from '../../../services/geoTools/getCellFromClick';
import {BasicTileLayer} from '../../../services/geoTools/tileLayers';
import Rainbow from '../../../services/rainbowvis/Rainbowvis';
import {renderAreaLayer, renderBoundaryOverlays, renderBoundingBoxLayer} from '../../t03/components/maps/mapLayers';
import {ColorLegend, ReactLeafletHeatMapCanvasOverlay} from '../rasterData';
import ContourLayer from '../rasterData/contourLayer';
import {
createGridData,
max,
min,
rainbowFactory
} from '../rasterData/helpers';
import {IReactLeafletHeatMapProps} from '../rasterData/ReactLeafletHeatMapCanvasOverlay.type';
import {FullscreenControl} from './index';
import Rainbow from '../../../services/rainbowvis/Rainbowvis';
import React, {useEffect, useRef, useState} from 'react';
import _ from 'lodash';
import uuid from 'uuid';

const style = {
map: {
Expand Down Expand Up @@ -67,6 +63,8 @@ interface IState {
viewport: Viewport | null;
}

const QUANTILE = 1;

const ResultsMap = (props: IProps) => {
const [state, setState] = useState<IState>({viewport: null});
const [renderKey, setRenderKey] = useState<string>(uuid.v4());
Expand Down Expand Up @@ -136,11 +134,11 @@ const ResultsMap = (props: IProps) => {
type: 'Polygon',
coordinates: [
[
[props.model.boundingBox.xMin, props.model.boundingBox.yMax - selectedRow * dY],
[props.model.boundingBox.xMax, props.model.boundingBox.yMax - selectedRow * dY],
[props.model.boundingBox.xMax, props.model.boundingBox.yMax - (selectedRow + 1) * dY],
[props.model.boundingBox.xMin, props.model.boundingBox.yMax - (selectedRow + 1) * dY],
[props.model.boundingBox.xMin, props.model.boundingBox.yMax - selectedRow * dY]
[props.model.boundingBox.xMin, props.model.boundingBox.yMax - (selectedRow * dY)],
[props.model.boundingBox.xMax, props.model.boundingBox.yMax - (selectedRow * dY)],
[props.model.boundingBox.xMax, props.model.boundingBox.yMax - ((selectedRow + 1) * dY)],
[props.model.boundingBox.xMin, props.model.boundingBox.yMax - ((selectedRow + 1) * dY)],
[props.model.boundingBox.xMin, props.model.boundingBox.yMax - (selectedRow * dY)]
]
]
}
Expand All @@ -152,11 +150,11 @@ const ResultsMap = (props: IProps) => {
type: 'Polygon',
coordinates: [
[
[props.model.boundingBox.xMin + selectedCol * dX, props.model.boundingBox.yMin],
[props.model.boundingBox.xMin + selectedCol * dX, props.model.boundingBox.yMax],
[props.model.boundingBox.xMin + (selectedCol + 1) * dX, props.model.boundingBox.yMax],
[props.model.boundingBox.xMin + (selectedCol + 1) * dX, props.model.boundingBox.yMin],
[props.model.boundingBox.xMin + selectedCol * dX, props.model.boundingBox.yMin]
[props.model.boundingBox.xMin + (selectedCol * dX), props.model.boundingBox.yMin],
[props.model.boundingBox.xMin + (selectedCol * dX), props.model.boundingBox.yMax],
[props.model.boundingBox.xMin + ((selectedCol + 1) * dX), props.model.boundingBox.yMax],
[props.model.boundingBox.xMin + ((selectedCol + 1) * dX), props.model.boundingBox.yMin],
[props.model.boundingBox.xMin + (selectedCol * dX), props.model.boundingBox.yMin]
]
]
}
Expand Down Expand Up @@ -203,8 +201,11 @@ const ResultsMap = (props: IProps) => {
return props.onViewPortChange(viewport);
};

let minData = min(props.data);
let maxData = max(props.data);
const filteredData = _.sortBy(_.flatten(props.data).filter((n) => n !== null));
const q = Math.floor(QUANTILE / 100 * filteredData.length);

let minData = filteredData[q];
let maxData = filteredData[filteredData.length - q];

if (props.globalMinMax) {
[minData, maxData] = props.globalMinMax;
Expand Down
Loading

0 comments on commit 75aea9f

Please sign in to comment.