Skip to content
GitLab
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
Nauta, Lisanne
Farmer Support Api
Commits
71386365
Commit
71386365
authored
May 30, 2022
by
Nauta, Lisanne
Browse files
Merge branch 'hotfix/unit_conv'
parents
c2682042
be31f4bf
Changes
8
Hide whitespace changes
Inline
Side-by-side
package.json
View file @
71386365
{
"name"
:
"api"
,
"version"
:
"0.4.
0
"
,
"version"
:
"0.4.
1
"
,
"description"
:
""
,
"author"
:
"lisanne.nauta@wur.nl"
,
"private"
:
true
,
...
...
src/data/models/data-response.ts
View file @
71386365
import
{
ApiExtraModels
,
ApiProperty
,
getSchemaPath
}
from
"
@nestjs/swagger
"
;
import
{
Point
}
from
"
geojson
"
;
import
{
Forecast
}
from
"
src/forecast/forecast.entity
"
;
import
{
Location
}
from
"
src/location/location.entity
"
;
import
{
Observation
}
from
"
src/observation/observation
.entity
"
;
import
{
LocalSoilMoistureForecastSeries
}
from
"
src/soil-moisture/soil-moisture-forecast/local-soil-moisture-forecast/local-soil-moisture-forecast-series
.entity
"
;
import
{
LocalSoilMoistureForecast
}
from
"
src/soil-moisture/soil-moisture-forecast/local-soil-moisture-forecast/local-soil-moisture-forecast.entity
"
;
import
{
LocalSoilMoistureForecastService
}
from
"
src/soil-moisture/soil-moisture-forecast/local-soil-moisture-forecast/local-soil-moisture-forecast.service
"
;
import
{
Variable
}
from
"
src/variable/variable.entity
"
;
import
{
EnsembleForecastTimeSeries
,
ForecastTimeSeries
,
TimeSeries
}
from
"
./time-series
"
;
import
{
TimeSeriesItem
}
from
"
./time-series-item
"
;
import
{
Point
}
from
'
geojson
'
;
@
ApiExtraModels
(
EnsembleForecastTimeSeries
,
ForecastTimeSeries
,
TimeSeries
,
Variable
)
export
class
DataResponse
{
...
...
@@ -89,17 +88,23 @@ export class DataResponse {
}
public
static
fromLocalSoilMoistureForecast
(
forecast
:
LocalSoilMoistureForecast
){
return
this
.
fromLocalSoilMoistureForecastSeries
(
forecast
.
series
,
forecast
.
cropfield
.
position
,
forecast
.
forecastdatetime
)
}
public
static
fromLocalSoilMoistureForecastSeries
(
series
:
LocalSoilMoistureForecastSeries
[],
position
:
Point
,
forecastDateTime
:
Date
){
const
variable
=
new
Variable
(
'
soil_moisture
'
,
'
soil moisture
'
,
'
cm3/cm3
'
);
const
location
=
new
Location
(
forecast
.
cropfield
.
position
);
const
location
=
new
Location
(
position
);
let
dataResponse
=
new
DataResponse
();
dataResponse
.
variables
=
[
variable
];
dataResponse
.
location
=
location
;
let
serie
s
=
[];
forecast
.
series
.
forEach
(
item
=>
{
let
t
s
=
[];
series
.
forEach
(
item
=>
{
let
timeSeriesItem
=
new
TimeSeriesItem
(
item
.
value
,
item
.
datetime
);
serie
s
.
push
(
timeSeriesItem
);
t
s
.
push
(
timeSeriesItem
);
});
const
data
=
new
ForecastTimeSeries
(
serie
s
,
forecast
.
forecastd
ate
t
ime
);
const
data
=
new
ForecastTimeSeries
(
t
s
,
forecast
D
ate
T
ime
);
dataResponse
.
data
[
variable
.
code
]
=
data
;
return
dataResponse
;
}
...
...
src/soil-moisture/soil-moisture-forecast/local-soil-moisture-forecast/dtos/list-local-forecast-query.ts
0 → 100644
View file @
71386365
import
{
Transform
}
from
"
class-transformer
"
;
import
{
IsBoolean
,
IsDateString
,
IsNumber
,
IsOptional
}
from
"
class-validator
"
;
export
class
ListLocalForecastQuery
{
@
IsOptional
()
@
IsDateString
()
minforecastdate
:
Date
;
@
IsOptional
()
@
IsDateString
()
maxforecastdate
:
Date
;
@
IsOptional
()
@
IsNumber
()
@
Transform
(({
value
})
=>
parseInt
(
value
))
userid
:
number
;
@
IsOptional
()
@
IsBoolean
()
latest
:
boolean
=
true
;
@
IsOptional
()
cropfieldid
:
number
;
}
\ No newline at end of file
src/soil-moisture/soil-moisture-forecast/local-soil-moisture-forecast/local-soil-moisture-forecast.entity.ts
View file @
71386365
...
...
@@ -26,7 +26,7 @@ export class LocalSoilMoistureForecast {
transformer
:{
to
:(
value
:
number
)
=>
{
if
(
value
!=
null
){
return
parseFloat
(
value
.
toFixed
(
2
))
return
parseFloat
(
value
.
toFixed
(
3
))
}
return
value
;
...
...
@@ -51,7 +51,7 @@ export class LocalSoilMoistureForecast {
if
(
value
!=
null
){
let
input
=
value
.
map
((
val
)
=>
{
if
(
val
!=
null
){
return
parseFloat
(
val
.
toFixed
(
2
))
return
parseFloat
(
val
.
toFixed
(
3
))
}
else
{
return
val
;
...
...
src/soil-moisture/soil-moisture-forecast/local-soil-moisture-forecast/local-soil-moisture-forecast.service.ts
View file @
71386365
import
{
forwardRef
,
Inject
,
Injectable
,
Logger
}
from
'
@nestjs/common
'
;
import
{
OnEvent
}
from
'
@nestjs/event-emitter
'
;
import
{
InjectRepository
}
from
'
@nestjs/typeorm
'
;
import
{
Point
}
from
'
geojson
'
;
import
{
DateTime
}
from
'
luxon
'
;
import
{
catchError
,
forkJoin
,
from
,
lastValueFrom
,
map
,
Observable
,
of
,
switchMap
,
tap
}
from
'
rxjs
'
;
import
{
CropField
}
from
'
src/crop-field/crop-field.entity
'
;
import
{
CropFieldService
}
from
'
src/crop-field/crop-field.service
'
;
import
{
Crop
}
from
'
src/crop/crop.entity
'
;
import
{
CropService
}
from
'
src/crop/crop.service
'
;
import
{
ForecastService
}
from
'
src/forecast/forecast.service
'
;
import
{
SoilMoistureService
}
from
'
src/soil-moisture/soil-moisture.service
'
;
import
{
SoilService
}
from
'
src/soil/soil.service
'
;
import
{
FindManyOptions
,
In
,
LessThan
,
LessThanOrEqual
,
MoreThanOrEqual
,
Repository
}
from
'
typeorm
'
;
import
{
User
}
from
'
src/user/user.entity
'
;
import
{
FindOneOptions
,
LessThan
,
LessThanOrEqual
,
MoreThanOrEqual
,
Repository
}
from
'
typeorm
'
;
import
{
ListLocalForecastQuery
}
from
'
./dtos/list-local-forecast-query
'
;
import
{
IrrigationTarget
}
from
'
./irrigation-target
'
;
import
{
LocalSoilMoistureForecastSeries
}
from
'
./local-soil-moisture-forecast-series.entity
'
;
import
{
LocalSoilMoistureForecast
}
from
'
./local-soil-moisture-forecast.entity
'
;
...
...
@@ -77,7 +79,7 @@ export class LocalSoilMoistureForecastService {
const
waitForRuns
=
forkJoin
(
runs
);
return
waitForRuns
;
}),
switchMap
((
forecasts
)
=>
{
Logger
.
debu
g
(
'
Save crop field forecasts.
'
,
LocalSoilMoistureForecastService
.
name
);
Logger
.
lo
g
(
'
Save crop field forecasts.
'
,
LocalSoilMoistureForecastService
.
name
);
// filter out failed crop field forecast runs
forecasts
=
forecasts
.
filter
(
f
=>
f
!=
null
);
// save bulk in chunks of 1000.
...
...
@@ -96,10 +98,6 @@ export class LocalSoilMoistureForecastService {
public
runCropFieldForecast
(
cropField
:
CropField
,
forecastDateTime
:
Date
,
initMoist
?:
number
,
irrigation
?:
number
[],
irrigationTarget
?:
IrrigationTarget
){
const
lon
=
cropField
.
position
.
coordinates
[
0
];
const
lat
=
cropField
.
position
.
coordinates
[
1
];
// get field capacity, wilting point for crop field coordinates
const
getSoilData
=
this
.
soilService
.
getSoilData
(
lon
,
lat
);
// get the meteo data for user region
const
getMeteoData
=
this
.
forecastService
.
findLatestForRegion
(
cropField
.
user
.
regionid
,[
'
prec
'
,
'
temp
'
],
forecastDateTime
);
/* this part set the initial soil moisture value with a fallback procedure.
1) given user initial condition
...
...
@@ -107,12 +105,16 @@ export class LocalSoilMoistureForecastService {
3) mid point of field capacity and wilting point. -> done in forecast days iteration */
let
getInitMoist
=
of
(
initMoist
);
if
(
!
initMoist
){
getInitMoist
=
from
(
this
.
findPreviousByCropFieldId
(
cropField
.
id
,
forecastDateTime
)).
pipe
(
map
(
forecast
=>
{
let
searchParams
=
{
cropfieldid
:
cropField
.
id
,
latest
:
true
}
as
ListLocalForecastQuery
getInitMoist
=
from
(
this
.
findOne
(
searchParams
)).
pipe
(
map
(
forecast
=>
{
let
value
=
null
;
if
(
forecast
){
// searches for the forecast date minus 1 day in last soil moisture forecast
const
i
=
forecast
.
series
.
findIndex
(
item
=>
item
.
datetime
.
getTime
()
===
forecastDateTime
.
getTime
());
if
(
i
!=-
1
){
if
(
i
>
0
){
value
=
forecast
.
series
[
i
-
1
].
value
;
}
}
...
...
@@ -120,17 +122,33 @@ export class LocalSoilMoistureForecastService {
}));
}
const
model
=
forkJoin
([
getSoilData
,
getMeteoData
,
getInitMoist
]).
pipe
(
map
(
res
=>
{
let
soilMoistVal
=
null
;
const
model
=
getInitMoist
.
pipe
(
switchMap
((
soilMoist
)
=>
{
soilMoistVal
=
soilMoist
return
this
.
runForecast
(
cropField
.
user
,
lon
,
lat
,
forecastDateTime
,
soilMoist
,
cropField
.
crop
,
cropField
.
plantdate
,
irrigation
,
irrigationTarget
)
}),
map
((
forecastSeries
)
=>
{
return
new
LocalSoilMoistureForecast
(
forecastSeries
,
cropField
.
id
,
forecastSeries
[
0
].
datetime
,
soilMoistVal
,
irrigation
,
irrigationTarget
);
}))
return
lastValueFrom
(
model
);
}
public
runForecast
(
user
:
User
,
lon
:
number
,
lat
:
number
,
forecastDateTime
:
Date
,
initMoisture
?:
number
,
crop
?:
Crop
,
plantDate
?:
Date
,
irrigation
?:
number
[],
irrigationTarget
?:
IrrigationTarget
){
// get field capacity, wilting point for crop field coordinates
const
getSoilData
=
this
.
soilService
.
getSoilData
(
lon
,
lat
);
// get the meteo data for user region
const
getMeteoData
=
this
.
forecastService
.
findLatestForRegion
(
user
.
regionid
,[
"
prec
"
,
"
temp
"
],
forecastDateTime
);
const
model
=
forkJoin
([
getSoilData
,
getMeteoData
]).
pipe
(
map
((
res
=>
{
const
soilData
=
res
[
0
];
const
maxInfilt
=
this
.
soilService
.
getMaxInfilt
(
soilData
.
tex_class
);
// TODO: soil data can be missing values
const
fc
=
soilData
.
fc
;
const
pwp
=
soilData
.
pwp
;
const
meteoData
=
res
[
1
];
let
getInitMoistVal
=
res
[
2
]
if
(
!
getInitMoistVal
){
getInitMoistVal
=
(
fc
+
pwp
)
/
2
;
}
// throw error, since we cannot produce forecasts without meteo data
if
(
meteoData
.
length
==
0
){
throw
new
Error
(
'
Meteo forecast not available
'
);
...
...
@@ -139,8 +157,12 @@ export class LocalSoilMoistureForecastService {
const
prec
=
meteoData
.
filter
(
item
=>
item
.
variablecode
==
'
prec
'
);
let
forecastSeries
=
[]
as
LocalSoilMoistureForecastSeries
[];
//forecastSeries.push(new LocalSoilMoistureForecastSeries(forecastDateTime,getInitMoistVal));
let
iInitMoist
=
getInitMoistVal
;
let
iInitMoist
=
initMoisture
;
if
(
!
iInitMoist
){
iInitMoist
=
(
fc
+
pwp
)
/
2
}
// interate lead times of meteo forecast and run soil moisture balance model.
for
(
let
i
=
0
;
i
<
temp
.
length
;
i
++
){
let
iIrrigation
=
0
;
...
...
@@ -165,21 +187,23 @@ export class LocalSoilMoistureForecastService {
const
iDoy
=
+
iDateTime
.
toFormat
(
'
o
'
);
const
Epot
=
this
.
soilMoistureService
.
getEpotHamon
(
temp
[
i
].
value
,
iDoy
,
lat
);
// estimate crop factor
const
kcValue
=
this
.
cropService
.
calcKcValue
(
cropField
.
crop
,
cropField
.
plantdate
,
temp
[
i
].
datetime
)
let
kcValue
=
1
;
if
(
crop
&&
plantDate
){
kcValue
=
this
.
cropService
.
calcKcValue
(
crop
,
plantDate
,
temp
[
i
].
datetime
)
}
const
ET
=
Epot
*
kcValue
;
iSoilMoist
=
this
.
soilMoistureService
.
calcBalance
(
iInitMoist
,
prec
[
i
].
value
,
ET
,
maxInfilt
,
fc
,
pwp
,
iIrrigation
);
}
const
iForecastSeries
=
new
LocalSoilMoistureForecastSeries
(
iDateTime
.
toJSDate
(),
iSoilMoist
);
iInitMoist
=
i
ForecastSeries
.
value
;
const
iSoilMoistVal
=
parseFloat
(
iSoilMoist
.
toFixed
(
3
))
const
iForecastSeries
=
new
LocalSoilMoistureForecastSeries
(
iDateTime
.
toJSDate
(),
iSoilMoist
Val
);
iInitMoist
=
i
SoilMoist
;
forecastSeries
.
push
(
iForecastSeries
);
}
let
soilMoistureForecast
=
new
LocalSoilMoistureForecast
(
forecastSeries
,
cropField
.
id
,
forecastDateTime
,
initMoist
,
irrigation
);
soilMoistureForecast
.
cropfieldid
=
cropField
.
id
;
return
soilMoistureForecast
;
}));
return
lastValueFrom
(
model
);
return
forecastSeries
;
})))
return
lastValueFrom
(
model
)
}
public
addForecast
(
forecast
:
LocalSoilMoistureForecast
){
...
...
@@ -215,35 +239,60 @@ export class LocalSoilMoistureForecastService {
return
this
.
cropFieldService
.
getDto
(
cropField
);
}
public
findPreviousByCropFieldId
(
cropFieldId
:
number
,
maxForecastDateTime
:
Date
){
const
findPrevious
=
this
.
repo
.
findOne
({
where
:{
cropfieldid
:
cropFieldId
,
forecastdatetime
:
LessThan
(
maxForecastDateTime
)
},
order
:{
forecastdatetime
:
'
DESC
'
}
});
return
findPrevious
private
getSelectQuery
(
params
:
ListLocalForecastQuery
){
let
query
=
this
.
repo
.
createQueryBuilder
(
"
forecast
"
);
query
.
leftJoinAndSelect
(
"
forecast.cropfield
"
,
"
cropfield
"
).
leftJoinAndSelect
(
"
cropfield.crop
"
,
"
crop
"
);
if
(
params
.
userid
){
query
.
where
(
"
cropfield.userid = :id
"
,{
id
:
params
.
userid
});
}
if
(
params
.
cropfieldid
){
query
.
where
(
"
forecast.cropfieldid = :id
"
,{
id
:
params
.
cropfieldid
});
}
if
(
params
.
minforecastdate
){
query
.
andWhere
(
"
forecastdatetime >= :minDate
"
,{
minDate
:
params
.
minforecastdate
});
}
if
(
params
.
maxforecastdate
){
query
.
andWhere
(
"
forecastdatetime <= :maxDate
"
,{
maxDate
:
params
.
maxforecastdate
});
}
if
(
params
.
latest
){
query
.
distinctOn
([
"
forecast.cropfieldid
"
]);
query
.
orderBy
({
'
forecast.cropfieldid
'
:
'
ASC
'
,
'
forecast.forecastdatetime
'
:
'
DESC
'
})
}
return
(
query
)
}
public
find
(
userId
?:
number
,
minForecastDate
?:
Date
,
maxForecastDate
?:
Date
){
let
query
=
{}
as
FindManyOptions
;
if
(
userId
){
query
.
relations
=
[
'
cropfield
'
]
query
.
where
=
{
cropfield
:
{
userid
:
userId
}
}
public
findOne
(
params
:
ListLocalForecastQuery
){
let
q
=
{}
as
FindOneOptions
;
q
.
relations
=
[
'
cropfield
'
];
q
.
where
=
{}
if
(
params
.
cropfieldid
){
q
.
where
[
'
cropfieldid
'
]
=
params
.
cropfieldid
;
}
if
(
min
F
orecast
D
ate
){
q
uery
.
where
[
'
forecastdate
time
'
]
=
MoreThanOrEqual
(
min
F
orecast
D
ate
);
if
(
params
.
min
f
orecast
d
ate
){
q
.
where
[
'
forecastdate
'
]
=
MoreThanOrEqual
(
params
.
min
f
orecast
d
ate
);
}
if
(
max
F
orecast
D
ate
){
q
uery
.
where
[
'
forecastdate
time
'
]
=
LessThanOrEqual
(
max
F
orecast
D
ate
);
if
(
params
.
max
f
orecast
d
ate
){
q
.
where
[
'
forecastdate
'
]
=
LessThanOrEqual
(
params
.
max
f
orecast
d
ate
);
}
return
this
.
repo
.
find
(
query
);
if
(
params
.
latest
){
q
.
order
=
{
forecastdatetime
:
'
DESC
'
}
}
return
this
.
repo
.
findOne
(
q
);
}
public
find
(
params
:
ListLocalForecastQuery
){
const
query
=
this
.
getSelectQuery
(
params
)
return
query
.
getMany
();
}
}
src/soil-moisture/soil-moisture.controller.ts
View file @
71386365
...
...
@@ -10,26 +10,19 @@ import { LocalSoilMoistureForecastService } from './soil-moisture-forecast/local
import
{
PutCropFieldSoilMoistureForecastDto
}
from
'
./soil-moisture-forecast/local-soil-moisture-forecast/dtos/put-crop-field-soil-moisture-forecast-dto
'
;
import
{
DataResponse
}
from
'
src/data/models/data-response
'
;
import
{
LocalSoilMoistureForecast
}
from
'
./soil-moisture-forecast/local-soil-moisture-forecast/local-soil-moisture-forecast.entity
'
;
import
{
IsDateString
,
IsNumber
,
IsOptional
}
from
'
class-validator
'
;
import
{
IsBoolean
,
IsDateString
,
IsNumber
,
IsOptional
}
from
'
class-validator
'
;
import
{
SoilService
}
from
'
src/soil/soil.service
'
;
import
{
Variable
}
from
'
src/variable/variable.entity
'
;
import
{
ForecastTimeSeries
}
from
'
src/data/models/time-series
'
;
import
{
TimeSeriesItem
}
from
'
src/data/models/time-series-item
'
;
import
{
Transform
}
from
'
class-transformer
'
;
import
{
CropFieldSoilMoistureForecastDto
}
from
'
./soil-moisture-forecast/local-soil-moisture-forecast/dtos/crop-field-soil-moisture-forecast-dto
'
;
import
{
EventEmitter2
}
from
'
@nestjs/event-emitter
'
;
import
{
Point
}
from
'
gdal-next
'
import
{
ListLocalForecastQuery
}
from
'
./soil-moisture-forecast/local-soil-moisture-forecast/dtos/list-local-forecast-query
'
;
const
PG_UNIQUE_CONSTRAINT_VIOLATION
=
"
23505
"
;
export
class
ListLocalForecastQuery
{
@
IsOptional
()
@
IsDateString
()
minforecastdate
:
Date
;
@
IsOptional
()
@
IsNumber
()
@
Transform
(({
value
})
=>
parseInt
(
value
))
userid
:
number
;
}
@
ApiTags
(
'
soil-moisture
'
)
@
Controller
(
'
soil-moisture
'
)
...
...
@@ -40,36 +33,6 @@ export class SoilMoistureController {
private
soilService
:
SoilService
){
}
/* @ApiResponse({
type:LocalSoilMoistureForecast
})
@UseInterceptors(ClassSerializerInterceptor)
@UseGuards(UserRoleGuard(UserRole.User))
@UsePipes(new ValidationPipe({ transform: true }))
@Post('local-forecast')
public async createLocalForecast(
@Req() request : Request, @Body() body: CropFieldSoilMoistureForecastDto){
const user = request.user as User;
if(body.cropField.userid != user.id){
throw new UnauthorizedException();
}
const now = DateTime.now();
const fdt = now.startOf('day').toJSDate();
const lon = body.cropField.position.coordinates[0];
const lat = body.cropField.position.coordinates[1];
const percentage = body.initMoistCondition? body.initMoistCondition : 50;
const soilData = await this.soilService.getSoilData(lon,lat);
const initMoist = await this.soilService.soilMoistCondition2Value(soilData[0].fc, soilData[0].pwp,percentage);
const response = from(this.soilMoistureForecastService.runCropFieldForecast(body.cropField,fdt,initMoist,body.irrigation)).pipe(switchMap((res)=>{
return this.soilMoistureForecastService.addForecast(res);
}),catchError((e:Error)=>{
throw new HttpException(e.message,500);
}))
return(response);
} */
@
UseInterceptors
(
ClassSerializerInterceptor
)
@
UseGuards
(
UserRoleGuard
(
UserRole
.
User
))
@
ApiParam
({
...
...
@@ -153,9 +116,58 @@ export class SoilMoistureController {
if
(
!
req
.
user
.
roles
.
includes
(
UserRole
.
Admin
)
&&
query
.
userid
!=
req
.
user
.
id
){
return
new
UnauthorizedException
();
}
return
this
.
soilMoistureForecastService
.
find
(
query
.
userid
,
query
.
minforecastdate
)
return
this
.
soilMoistureForecastService
.
find
(
query
)
;
}
@
ApiResponse
({
type
:
DataResponse
})
@
ApiQuery
({
name
:
'
init_condition
'
,
type
:
Number
,
required
:
true
})
@
ApiQuery
({
name
:
'
lon
'
,
type
:
Number
,
required
:
true
})
@
ApiQuery
({
name
:
'
lat
'
,
type
:
Number
,
required
:
true
})
@
UseGuards
(
UserRoleGuard
(
UserRole
.
User
))
@
Get
(
'
forecast-local/data
'
)
async
getLocalForcastData
(@
Req
()
request
:
Request
,@
Query
(
'
init_condition
'
)
initCondition
:
number
,
@
Query
(
'
lon
'
)
lon
:
number
,@
Query
(
'
lat
'
)
lat
:
number
){
const
user
=
request
.
user
as
User
;
const
fdt
=
DateTime
.
now
().
minus
({
days
:
1
}).
toJSDate
();
const
soilData
=
await
this
.
soilService
.
getSoilData
(
lon
,
lat
);
const
initMoisture
=
this
.
soilService
.
soilMoistCondition2Value
(
soilData
.
fc
,
soilData
.
pwp
,
initCondition
);
const
forecastSeries
=
await
this
.
soilMoistureForecastService
.
runForecast
(
user
,
lon
,
lat
,
fdt
,
initMoisture
);
const
position
=
new
Point
(
lon
,
lat
)
let
dataResponse
=
DataResponse
.
fromLocalSoilMoistureForecastSeries
(
forecastSeries
,
position
,
forecastSeries
[
0
].
datetime
);
const
conditionVariable
=
new
Variable
(
"
soil_moisture_condition
"
,
"
soil moisture condition
"
,
"
%
"
)
const
soilMoistureForecast
=
dataResponse
.
data
[
"
soil_moisture
"
]
as
ForecastTimeSeries
;
var
conditionSeries
:
TimeSeriesItem
[]
=
[];
soilMoistureForecast
.
series
.
forEach
(
item
=>
{
const
newValue
=
this
.
soilService
.
soilMoistValue2Condition
(
soilData
.
fc
,
soilData
.
pwp
,
item
.
value
);
conditionSeries
.
push
(
new
TimeSeriesItem
(
newValue
,
item
.
timestamp
));
});
dataResponse
.
variables
.
push
(
conditionVariable
);
dataResponse
.
data
[
"
soil_moisture_condition
"
]
=
new
ForecastTimeSeries
(
conditionSeries
,
soilMoistureForecast
.
forecastDate
);
return
dataResponse
;
}
@
ApiResponse
({
type
:
DataResponse
})
...
...
@@ -178,11 +190,11 @@ export class SoilMoistureController {
const
soilData
=
await
this
.
soilService
.
getSoilData
(
lon
,
lat
);
var
dataReponse
=
DataResponse
.
fromLocalSoilMoistureForecast
(
forecast
);
var
dataRe
s
ponse
=
DataResponse
.
fromLocalSoilMoistureForecast
(
forecast
);
const
conditionVariable
=
new
Variable
(
"
soil_moisture_condition
"
,
"
soil moisture condition
"
,
"
%
"
)
const
soilMoistureForecast
=
dataReponse
.
data
[
"
soil_moisture
"
]
as
ForecastTimeSeries
;
const
soilMoistureForecast
=
dataRe
s
ponse
.
data
[
"
soil_moisture
"
]
as
ForecastTimeSeries
;
var
conditionSeries
:
TimeSeriesItem
[]
=
[];
soilMoistureForecast
.
series
.
forEach
(
item
=>
{
...
...
@@ -190,11 +202,11 @@ export class SoilMoistureController {
conditionSeries
.
push
(
new
TimeSeriesItem
(
newValue
,
item
.
timestamp
));
});
dataReponse
.
variables
.
push
(
conditionVariable
);
dataReponse
.
data
[
"
soil_moisture_condition
"
]
=
new
ForecastTimeSeries
(
conditionSeries
,
soilMoistureForecast
.
forecastDate
)
dataRe
s
ponse
.
variables
.
push
(
conditionVariable
);
dataRe
s
ponse
.
data
[
"
soil_moisture_condition
"
]
=
new
ForecastTimeSeries
(
conditionSeries
,
soilMoistureForecast
.
forecastDate
)
return
dataReponse
;
return
dataRe
s
ponse
;
}
}
...
...
src/soil-moisture/soil-moisture.service.ts
View file @
71386365
...
...
@@ -24,8 +24,8 @@ export class SoilMoistureService {
}
//calc soil moist for timestamp
let
soilMoist
=
initMoist
*
100
+
precInfilt
// distribute excess water over 1m top soil
soilMoist
=
soilMoist
/
100
// back to [cm3/cm3]
let
soilMoist
=
initMoist
*
100
0
+
precInfilt
// distribute excess water over 1m top soil
soilMoist
=
soilMoist
/
100
0
// back to
fraction
[cm3/cm3]
//check against fc and pwp
if
(
soilMoist
<
pwp
){
soilMoist
=
pwp
;
...
...
@@ -33,7 +33,7 @@ export class SoilMoistureService {
if
(
soilMoist
>
fc
){
soilMoist
=
fc
}
return
(
parseFloat
(
soilMoist
.
toFixed
(
2
))
)
return
(
soilMoist
)
}
public
getMaxInfilt
(
tex_class
:
number
){
...
...
src/soil/soil.service.ts
View file @
71386365
...
...
@@ -55,12 +55,12 @@ export class SoilService {
public
soilMoistCondition2Value
(
fc
:
number
,
pwp
:
number
,
percentage
:
number
){
const
value
=
pwp
+
(
fc
-
pwp
)
*
(
percentage
/
100
);
const
value
=
pwp
+
(
fc
-
pwp
)
*
(
percentage
/
100
);
// returns soil moisture fraction from a percentage
return
value
;
}
public
soilMoistValue2Condition
(
fc
:
number
,
pwp
:
number
,
soilMoisture
:
number
){
const
condition
=
((
soilMoisture
-
pwp
)
/
(
fc
-
pwp
))
*
100
;
const
condition
=
((
soilMoisture
-
pwp
)
/
(
fc
-
pwp
))
*
100
;
// return a percentage soil moisture between field capacity and wilting point
return
condition
;
}
...
...
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment