Compare commits
9 Commits
main
...
ole/shaver
| Author | SHA1 | Date | |
|---|---|---|---|
|
9c7a30ca1c
|
|||
|
54a4a2cd5c
|
|||
|
dff72d1ddb
|
|||
|
05161b0761
|
|||
|
bcab9f974d
|
|||
|
0042b10935
|
|||
|
dac7b0a858
|
|||
|
5db721f80f
|
|||
|
092b06b75b
|
4
.gitignore
vendored
4
.gitignore
vendored
@@ -23,4 +23,6 @@ tilt/base/_manifest.yaml
|
||||
NuGet.Config
|
||||
sync.list
|
||||
packages.lock.json
|
||||
package-lock.json
|
||||
package-lock.json
|
||||
src/Atlantis/.direnv
|
||||
src/Sorcerer/.direnv
|
||||
|
||||
@@ -60,6 +60,7 @@
|
||||
"@spectrum-web-components/underlay": "^1.0.3",
|
||||
"@turf/bezier-spline": "^7.1.0",
|
||||
"@vaadin/login": "^24.4.9",
|
||||
"html2canvas": "^1.4.1",
|
||||
"lit": "^3.1.0",
|
||||
"lit-html": "^3.1.0",
|
||||
"ol": "^10.1.0",
|
||||
@@ -70,4 +71,4 @@
|
||||
"react-plotly.js": "^2.6.0",
|
||||
"vis-timeline": "^7.7.2"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,6 +59,7 @@ importSideEffects "@spectrum-web-components/dialog/sp-dialog.js"
|
||||
importSideEffects "@spectrum-web-components/infield-button/sp-infield-button.js"
|
||||
importSideEffects "@spectrum-web-components/icons-workflow/icons/sp-icon-add.js"
|
||||
importSideEffects "@spectrum-web-components/icons-workflow/icons/sp-icon-add-circle.js"
|
||||
importSideEffects "@spectrum-web-components/icons-workflow/icons/sp-icon-airplane.js"
|
||||
importSideEffects "@spectrum-web-components/icons-workflow/icons/sp-icon-alert.js"
|
||||
importSideEffects "@spectrum-web-components/icons-workflow/icons/sp-icon-prototyping.js"
|
||||
importSideEffects "@spectrum-web-components/icons-workflow/icons/sp-icon-close.js"
|
||||
@@ -103,3 +104,4 @@ importSideEffects "@spectrum-web-components/icons-workflow/icons/sp-icon-social-
|
||||
importSideEffects "@spectrum-web-components/icons-workflow/icons/sp-icon-checkmark.js"
|
||||
importSideEffects "@spectrum-web-components/icons-workflow/icons/sp-icon-rotate-cc-w.js"
|
||||
importSideEffects "@spectrum-web-components/icons-workflow/icons/sp-icon-align-bottom.js"
|
||||
importSideEffects "@spectrum-web-components/icons-workflow/icons/sp-icon-camera.js"
|
||||
@@ -62,6 +62,7 @@ type FvcomApi(url) =
|
||||
|
||||
member val Archive = createApi Remoting.buildProxy<Api.Fvcom.Archive>
|
||||
member val Layer = createBinApi Remoting.buildProxy<Api.Fvcom.Layer>
|
||||
member val CompressedLayer = createBinApi Remoting.buildProxy<Api.Fvcom.CompressedLayer>
|
||||
member val Node = createBinApi Remoting.buildProxy<Api.Fvcom.Node>
|
||||
member val Element = createBinApi Remoting.buildProxy<Api.Fvcom.Element>
|
||||
member val Batch = createBinApi Remoting.buildProxy<Api.Fvcom.Batch>
|
||||
|
||||
@@ -18,6 +18,12 @@ open Maps
|
||||
let sideEffectCmd fn arg = Elmish.Cmd.OfPromise.perform fn arg Noop
|
||||
let sideEffectCmd' (fn: unit -> unit) = Elmish.Cmd.OfPromise.perform (fn >> Promise.lift) () Noop
|
||||
|
||||
[<Emit("performance.now()")>]
|
||||
let perfNow () = jsNative
|
||||
|
||||
/// Used to gather timings of variable fetch -> drawn layer
|
||||
let mutable t0: float = 0.0
|
||||
|
||||
let private logPos x = if x < 1.e-10f then -10.f else x |> float |> Math.Log10 |> single
|
||||
let private logNeg x = if x < -1.e10f then -10.f else -x |> float |> Math.Log10 |> single
|
||||
|
||||
@@ -40,6 +46,7 @@ let updateWebglLayer model prop (layer: MapLayer) =
|
||||
webglLayer.updateOpacity prop.Alpha
|
||||
webglLayer.updateProps data
|
||||
source.refresh ()
|
||||
console.debug $"time: {(perfNow ()) - t0}"
|
||||
)
|
||||
}
|
||||
|> Async.StartImmediate
|
||||
@@ -819,6 +826,7 @@ let oceanControls dispatch model =
|
||||
let toggleWindBarbs _ = dispatch (ShowWindBarbs(not model.showWindBarbs))
|
||||
let toggleWhiteWindBarbs _ = dispatch (SetWhiteWindBarbs(not model.whiteWindBarbs))
|
||||
let toggleStreams _ = (ShowStreams >> dispatch) (not model.showStreams)
|
||||
let toggleCompressed _ = (UseCompressedProps >> dispatch) (not model.compressedProps)
|
||||
|
||||
let windSlider =
|
||||
html $"""
|
||||
@@ -835,6 +843,40 @@ let oceanControls dispatch model =
|
||||
> </sp-slider>
|
||||
"""
|
||||
|
||||
let flyToLatLon (lat, lon) (zoom) =
|
||||
let c = Utils.toEpsg3857' (lon, lat)
|
||||
flyTo model.map zoom [| fst c; snd c|]
|
||||
|
||||
let NappSamples = [|
|
||||
(68.12, 13.593, 13.5)
|
||||
(68.05, 13.55, 13.5)
|
||||
(68.2, 13.3, 11.6)
|
||||
(68.145, 13.458, 11.0)
|
||||
(68.145, 13.458, 13.0)
|
||||
(67.98, 13.56, 12.)
|
||||
|]
|
||||
|
||||
let PO5Samples = [|
|
||||
(62.68, 7.15, 11.0)
|
||||
(63.09, 6.33, 10.0)
|
||||
(62.51, 5.94, 12.0)
|
||||
(62.37, 6.15, 12.6)
|
||||
(62.40, 5.33, 11.0)
|
||||
(61.95, 5.09, 12.0)
|
||||
|]
|
||||
|
||||
let destButton (dest: float * float) (zoom: float) (num: int) =
|
||||
html $"""
|
||||
<sp-action-button
|
||||
@click="{Ev(fun _ -> flyToLatLon dest zoom)}"
|
||||
id="flyto-button"
|
||||
value="flyto"
|
||||
size="m"
|
||||
>
|
||||
{num}
|
||||
</sp-action-button>
|
||||
"""
|
||||
|
||||
html $"""
|
||||
<div
|
||||
slot="content"
|
||||
@@ -859,6 +901,13 @@ let oceanControls dispatch model =
|
||||
<sp-radio ?disabled={disabled} value="{Prop.Speed}" @change={Ev(setProp Prop.Speed)}>Speed</sp-radio>
|
||||
<sp-radio ?disabled={disabled} value="{Prop.Bathy}" @change={Ev(setProp Prop.Bathy)}>Depth</sp-radio>
|
||||
</sp-radio-group>
|
||||
<sp-switch label="ZFP"
|
||||
?disabled={disabled}
|
||||
?checked={model.compressedProps}
|
||||
@click={Ev(toggleCompressed)}
|
||||
>
|
||||
ZFP
|
||||
</sp-switch>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
@@ -904,6 +953,44 @@ let oceanControls dispatch model =
|
||||
{if model.showWindBarbs then windSlider else Lit.nothing}
|
||||
</div>
|
||||
</div>
|
||||
<div style="margin: 10px;">
|
||||
<sp-field-label size="s" for="napp-samples">
|
||||
Napp Samples
|
||||
</sp-field-label>
|
||||
<div style="
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
max-width: 320;
|
||||
gap: 5px;
|
||||
">
|
||||
|
||||
{
|
||||
NappSamples
|
||||
|> Array.mapi (fun i (lon, lat, zoom) ->
|
||||
destButton (lon, lat) zoom i
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div style="margin: 10px;">
|
||||
<sp-field-label size="s" for="PO5-samples">
|
||||
PO5 Samples
|
||||
</sp-field-label>
|
||||
<div style="
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
max-width: 320;
|
||||
gap: 5px;
|
||||
">
|
||||
|
||||
{
|
||||
PO5Samples
|
||||
|> Array.mapi (fun i (lon, lat, zoom) ->
|
||||
destButton (lon, lat) zoom i
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
"""
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -236,6 +236,7 @@ type Model = {
|
||||
showWindBarbs: bool
|
||||
whiteWindBarbs: bool
|
||||
surface: bool
|
||||
compressedProps: bool // Whether to fetch zfp-compressed slices
|
||||
customGrid: CircleGrid option
|
||||
auxGrid: PlainGrid option
|
||||
|
||||
@@ -313,6 +314,7 @@ with
|
||||
contourCutoff = Map [0, (false, 330.0); 1, (false, 10.0); 2, (false, 100.0); 3, (false, 10_000.0)]
|
||||
contourKind = FullContour
|
||||
surface = true
|
||||
compressedProps = false
|
||||
showWindBarbs = false
|
||||
whiteWindBarbs = false
|
||||
isLoading = None
|
||||
@@ -365,6 +367,7 @@ type Msg =
|
||||
| InitOcean of ViewProp
|
||||
| InitLayer of (MapLayer * ViewProp)
|
||||
| ShowSurface of bool
|
||||
| UseCompressedProps of bool
|
||||
| ShowGrid of bool
|
||||
| ShowWindBarbs of bool
|
||||
| SetWhiteWindBarbs of bool
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
module Probing
|
||||
|
||||
open Browser
|
||||
open Fable.Core
|
||||
open Fable.Core.JsInterop
|
||||
open Fable.OpenLayers
|
||||
open Lit
|
||||
@@ -305,6 +306,56 @@ let backButton model dispatch =
|
||||
</div>
|
||||
"""
|
||||
|
||||
[<ImportDefault("html2canvas")>]
|
||||
[<Emit("html2canvas($1)")>]
|
||||
let html2canvas (element: Types.HTMLElement) : JS.Promise<Types.HTMLCanvasElement> = jsNative
|
||||
|
||||
let openScreenshot (c: Types.Element) =
|
||||
emitJsExpr (c) "window.open('', $0.toDataURL())"
|
||||
|
||||
[<ImportDefault("html2canvas")>]
|
||||
[<Emit("html2canvas($1).then($2)")>]
|
||||
let html2canvasThen e f = jsNative
|
||||
|
||||
// TODO: Implement this properly
|
||||
let screenshotButton (model: Model) =
|
||||
|
||||
let takeScreenshot () =
|
||||
// let f arg =
|
||||
// arg |> openScreenshot
|
||||
// let mapDiv = document.getElementById("map-container")
|
||||
// html2canvasThen mapDiv f
|
||||
|
||||
// let l = document.getElementsByTagName("canvas")
|
||||
// console.log(l)
|
||||
// let canvas = l[0]
|
||||
// canvas |> openScreenshot
|
||||
|
||||
let canvas = model.map.getViewport().querySelectorAll(".ol-layer canvas, canvas.ol-layer")
|
||||
console.log canvas
|
||||
let canvas' = canvas[0]
|
||||
canvas' |> openScreenshot
|
||||
|
||||
// promise {
|
||||
// console.log("starting promise...")
|
||||
// let mapDiv = document.getElementById("map-container")
|
||||
// console.log(mapDiv)
|
||||
// let! canvas = html2canvas mapDiv
|
||||
// canvas |> openScreenshot
|
||||
// console.log(canvas)
|
||||
// } |> Promise.start
|
||||
|
||||
html $"""
|
||||
<sp-action-button
|
||||
@click="{Ev(fun _ -> takeScreenshot ())}"
|
||||
id="screenshot-button"
|
||||
value="screenshot"
|
||||
size="m"
|
||||
>
|
||||
<sp-icon-camera slot="icon"></sp-icon-camera>
|
||||
</sp-actio n-button>
|
||||
"""
|
||||
|
||||
// TODO: Rename to something more general like topbar or something
|
||||
/// <summary>
|
||||
/// A selection of button toggles that enables tooling and interactions against the map. Currently there
|
||||
@@ -381,6 +432,7 @@ let measures (model: Model) dispatch =
|
||||
|
||||
<sp-action-group class="measures-button-group">
|
||||
{Drifters.driftersInputModal model.selectedDrifters (CloneDriftersInput >> dispatch)}
|
||||
{screenshotButton model}
|
||||
</sp-action-group>
|
||||
</div>
|
||||
"""
|
||||
|
||||
@@ -240,6 +240,8 @@
|
||||
fragColor = color;
|
||||
}
|
||||
</script>
|
||||
<script src="js/decomp_wasm.js"></script>
|
||||
<script src="js/zfp_decomp.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
21
src/Atlantis/src/Client/public/js/decomp_wasm.js
Normal file
21
src/Atlantis/src/Client/public/js/decomp_wasm.js
Normal file
File diff suppressed because one or more lines are too long
BIN
src/Atlantis/src/Client/public/js/decomp_wasm.wasm
Executable file
BIN
src/Atlantis/src/Client/public/js/decomp_wasm.wasm
Executable file
Binary file not shown.
29
src/Atlantis/src/Client/public/js/zfp_decomp.js
Normal file
29
src/Atlantis/src/Client/public/js/zfp_decomp.js
Normal file
@@ -0,0 +1,29 @@
|
||||
/**
|
||||
* Decompress the compressed data given by `compressedData`. Assumes target is 1d double array.
|
||||
* @param {compressedData} Uint8Array containing the compressed data
|
||||
* @param {originalLength} the original size of the uncompressed double array
|
||||
* @param {tolerance} Inaccuracy tolerance used in compression
|
||||
* @returns {Float32Array} Decompressed data
|
||||
*/
|
||||
async function decompress(compressedData, originalLength, tolerance) {
|
||||
// Wait for wasm module to load
|
||||
const Zfp = await Module();
|
||||
|
||||
const t0 = performance.now();
|
||||
// malloc and memcpy compressed array into wasm heap
|
||||
let compressed_p = Zfp._malloc(compressedData.length);
|
||||
Zfp.HEAP8.set(compressedData, compressed_p);
|
||||
|
||||
// Run decompression
|
||||
let new_data_p = Zfp._decompress(
|
||||
compressed_p,
|
||||
compressedData.length,
|
||||
originalLength,
|
||||
tolerance,
|
||||
);
|
||||
|
||||
const t = performance.now();
|
||||
console.log(`decompress: ${t - t0}`);
|
||||
// Retrieve uncompressed data from wasm heap
|
||||
return Zfp.HEAPF32.subarray(new_data_p / 4, new_data_p / 4 + originalLength);
|
||||
}
|
||||
@@ -32,6 +32,16 @@ module Api =
|
||||
GetVNode: Guid -> FrameIdx -> LayerIdx -> Async<single array>
|
||||
}
|
||||
|
||||
type CompressedLayer = {
|
||||
GetBathymetry: Guid -> Async<byte array>
|
||||
GetSalinity: Guid -> FrameIdx -> LayerIdx -> Async<byte array>
|
||||
GetTemperature: Guid -> FrameIdx -> LayerIdx -> Async<byte array>
|
||||
GetZeta: Guid -> FrameIdx -> Async<byte array>
|
||||
GetSpeed: Guid -> FrameIdx -> LayerIdx -> Async<byte array>
|
||||
GetUNode: Guid -> FrameIdx -> LayerIdx -> Async<byte array>
|
||||
GetVNode: Guid -> FrameIdx -> LayerIdx -> Async<byte array>
|
||||
}
|
||||
|
||||
type Node = {
|
||||
GetSiglev: Guid -> NodeIdx -> Async<single array>
|
||||
GetSiglay: Guid -> NodeIdx -> Async<single array>
|
||||
@@ -69,38 +79,84 @@ module Api =
|
||||
|
||||
let routeBuilder = routeBuilder "drifters"
|
||||
|
||||
type Archive = {
|
||||
GetNumFrames: Guid -> Async<int>
|
||||
GetSimType: Guid -> Async<DriftersVariant>
|
||||
}
|
||||
type Archive = { GetNumFrames: Guid -> Async<int>; GetSimType: Guid -> Async<DriftersVariant> }
|
||||
|
||||
type Particles = {
|
||||
GetFrame: Guid -> FrameIdx -> Async<ApiParticle[]>
|
||||
GetReleaseSites: Guid -> Async<Site[]>
|
||||
}
|
||||
type Particles = { GetFrame: Guid -> FrameIdx -> Async<ApiParticle[]>; GetReleaseSites: Guid -> Async<Site[]> }
|
||||
|
||||
type Network = {
|
||||
GetReleaseSites: Guid -> Async<{| coord: float * float; radius: float |}[]>
|
||||
GetConnectionMatrix: Guid -> FrameIdx -> FieldKind -> GroupKind list -> ParticleKind list -> ParticleState -> Async<float[][]>
|
||||
GetConnectionMatrix:
|
||||
Guid
|
||||
-> FrameIdx
|
||||
-> FieldKind
|
||||
-> GroupKind list
|
||||
-> ParticleKind list
|
||||
-> ParticleState
|
||||
-> Async<float[][]>
|
||||
}
|
||||
|
||||
type Sedimentation = {
|
||||
GetField: Guid -> FrameIdx -> FieldKind -> SedimentKind -> Async<single[]>
|
||||
GetContour: Guid -> FrameIdx -> FieldKind -> SedimentKind -> ContourKind -> double -> Async<(float*float) [][]>
|
||||
GetContour:
|
||||
Guid -> FrameIdx -> FieldKind -> SedimentKind -> ContourKind -> double -> Async<(float * float)[][]>
|
||||
GetSeries: Guid -> FrameIdx * FrameIdx -> FieldKind -> SedimentKind -> NodeIdx -> Async<single[]>
|
||||
}
|
||||
|
||||
type Field2D = {
|
||||
GetWaterContactField: Guid -> FrameIdx -> FieldKind -> GroupKind list -> ParticleKind list -> ParticleState -> Async<single[]>
|
||||
GetWaterContactSeries: Guid -> FrameIdx * FrameIdx -> FieldKind -> GroupKind list -> ParticleKind list -> ParticleState -> NodeIdx -> Async<single[]>
|
||||
GetField: Guid -> FrameIdx -> FieldKind -> GroupKind list -> ParticleKind list -> ParticleState -> Async<single[]>
|
||||
GetSeries: Guid -> FrameIdx * FrameIdx -> FieldKind -> GroupKind list -> ParticleKind list -> ParticleState -> NodeIdx -> Async<single[]>
|
||||
GetWaterContactField:
|
||||
Guid -> FrameIdx -> FieldKind -> GroupKind list -> ParticleKind list -> ParticleState -> Async<single[]>
|
||||
GetWaterContactSeries:
|
||||
Guid
|
||||
-> FrameIdx * FrameIdx
|
||||
-> FieldKind
|
||||
-> GroupKind list
|
||||
-> ParticleKind list
|
||||
-> ParticleState
|
||||
-> NodeIdx
|
||||
-> Async<single[]>
|
||||
GetField:
|
||||
Guid -> FrameIdx -> FieldKind -> GroupKind list -> ParticleKind list -> ParticleState -> Async<single[]>
|
||||
GetSeries:
|
||||
Guid
|
||||
-> FrameIdx * FrameIdx
|
||||
-> FieldKind
|
||||
-> GroupKind list
|
||||
-> ParticleKind list
|
||||
-> ParticleState
|
||||
-> NodeIdx
|
||||
-> Async<single[]>
|
||||
}
|
||||
|
||||
type Field3D = {
|
||||
GetField: Guid -> FrameIdx -> FieldKind -> GroupKind list -> ParticleKind list -> ParticleState -> LayerIdx list -> Async<single[]>
|
||||
GetContour: Guid -> FrameIdx -> FieldKind -> GroupKind list -> ParticleKind list -> LayerIdx list -> ContourKind -> double -> Async<(float*float) [][]>
|
||||
GetSeries: Guid -> FrameIdx * FrameIdx -> FieldKind -> GroupKind list -> ParticleKind list -> ParticleState -> LayerIdx list -> NodeIdx -> Async<single[]>
|
||||
GetField:
|
||||
Guid
|
||||
-> FrameIdx
|
||||
-> FieldKind
|
||||
-> GroupKind list
|
||||
-> ParticleKind list
|
||||
-> ParticleState
|
||||
-> LayerIdx list
|
||||
-> Async<single[]>
|
||||
GetContour:
|
||||
Guid
|
||||
-> FrameIdx
|
||||
-> FieldKind
|
||||
-> GroupKind list
|
||||
-> ParticleKind list
|
||||
-> LayerIdx list
|
||||
-> ContourKind
|
||||
-> double
|
||||
-> Async<(float * float)[][]>
|
||||
GetSeries:
|
||||
Guid
|
||||
-> FrameIdx * FrameIdx
|
||||
-> FieldKind
|
||||
-> GroupKind list
|
||||
-> ParticleKind list
|
||||
-> ParticleState
|
||||
-> LayerIdx list
|
||||
-> NodeIdx
|
||||
-> Async<single[]>
|
||||
}
|
||||
|
||||
type FieldMetaData = {
|
||||
@@ -118,7 +174,8 @@ module Api =
|
||||
/// <summary>
|
||||
/// Fetches wind velocities from the given archive at a given time 't', with a number of arrows 'n', from tile map coordinates 'x', 'y', and 'z'
|
||||
/// </summary>
|
||||
WindTile: Guid -> int (*t*) -> int (*n*)-> int (*z*) -> int (*x*) -> int (*y*) -> Async<V2<single> option array>
|
||||
WindTile:
|
||||
Guid -> int (*t*) -> int (*n*) -> int (*z*) -> int (*x*) -> int (*y*) -> Async<V2<single> option array>
|
||||
/// <summary>
|
||||
/// Fetches json that describes barb shapes and their corresponding speeds to which they should be drawn.
|
||||
/// </summary>
|
||||
@@ -160,41 +217,48 @@ module Api =
|
||||
type FvStatsSeries = {
|
||||
GetSalinity: Guid -> StatMetric -> Layer -> NodeIdx -> Async<single[]>
|
||||
GetTemperature: Guid -> StatMetric -> Layer -> NodeIdx -> Async<single[]>
|
||||
GetSpeed: Guid -> StatMetric -> Layer -> ElemIdx -> Async<single[]>
|
||||
GetSpeed: Guid -> StatMetric -> Layer -> ElemIdx -> Async<single[]>
|
||||
}
|
||||
|
||||
module Crop =
|
||||
let routeBuilder = routeBuilder "crop"
|
||||
|
||||
type Fvcom = {
|
||||
CropTemp: Guid -> int -> float * float -> float * float -> int * int -> float -> Async<single[]>
|
||||
CropSalinity: Guid -> int -> float * float -> float * float -> int * int -> float -> Async<single[]>
|
||||
CropSpeed: Guid -> int -> float * float -> float * float -> int * int -> float -> Async<single[]>
|
||||
CropUV: Guid -> int -> float * float -> float * float -> int * int -> float -> Async<(single * single)[]>
|
||||
CropTemp: Guid -> int -> float * float -> float * float -> int * int -> float -> Async<single[]>
|
||||
CropSalinity: Guid -> int -> float * float -> float * float -> int * int -> float -> Async<single[]>
|
||||
CropSpeed: Guid -> int -> float * float -> float * float -> int * int -> float -> Async<single[]>
|
||||
CropUV: Guid -> int -> float * float -> float * float -> int * int -> float -> Async<(single * single)[]>
|
||||
}
|
||||
|
||||
type Stats = {
|
||||
CropTemp: Guid -> Period -> StatMetric -> float * float -> float * float -> int * int -> float -> Async<single[]>
|
||||
CropSalinity: Guid -> Period -> StatMetric -> float * float -> float * float -> int * int -> float -> Async<single[]>
|
||||
CropSpeed: Guid -> Period -> StatMetric -> float * float -> float * float -> int * int -> float -> Async<single[]>
|
||||
CropUV: Guid -> Period -> StatMetric -> float * float -> float * float -> int * int -> float -> Async<(single * single)[]>
|
||||
CropTemp:
|
||||
Guid -> Period -> StatMetric -> float * float -> float * float -> int * int -> float -> Async<single[]>
|
||||
CropSalinity:
|
||||
Guid -> Period -> StatMetric -> float * float -> float * float -> int * int -> float -> Async<single[]>
|
||||
CropSpeed:
|
||||
Guid -> Period -> StatMetric -> float * float -> float * float -> int * int -> float -> Async<single[]>
|
||||
CropUV:
|
||||
Guid
|
||||
-> Period
|
||||
-> StatMetric
|
||||
-> float * float
|
||||
-> float * float
|
||||
-> int * int
|
||||
-> float
|
||||
-> Async<(single * single)[]>
|
||||
}
|
||||
|
||||
module Ocean =
|
||||
let routeBuilder = routeBuilder "ocean"
|
||||
|
||||
type PointRequest = {
|
||||
aid: Guid
|
||||
coord: float * float
|
||||
time: DateTime
|
||||
}
|
||||
type PointRequest = { aid: Guid; coord: float * float; time: DateTime }
|
||||
|
||||
type TimeseriesRequest = {
|
||||
aid: Guid
|
||||
coord: float * float
|
||||
depth: float
|
||||
time: DateTime
|
||||
days: int
|
||||
aid: Guid
|
||||
coord: float * float
|
||||
depth: float
|
||||
time: DateTime
|
||||
days: int
|
||||
}
|
||||
|
||||
type Property =
|
||||
|
||||
@@ -295,6 +295,7 @@ module Fvcom =
|
||||
Log.Information("sorcerer: grid: user {username} -> {RequestPath}", ctx.User.Identity.Name, ctx.Request.Path)
|
||||
let uid = ctx.User.Identity.Name
|
||||
let observer = ctx.GetService<ObserverFactory>().Create(uid, tag="Fvcom.layer")
|
||||
let s = Diagnostics.Stopwatch ()
|
||||
{
|
||||
GetBathymetry =
|
||||
fun aid ->
|
||||
@@ -308,38 +309,52 @@ module Fvcom =
|
||||
GetSalinity =
|
||||
fun aid t l ->
|
||||
async {
|
||||
let logData = {| aid = aid; t = t; l = l |}
|
||||
use _ = observer.trace ("GetSalinity", "{@log_data}", logData)
|
||||
return
|
||||
s.Start ()
|
||||
let salt =
|
||||
Fvcom.salinity aid t l
|
||||
|> Result.defaultValue Array.empty
|
||||
s.Stop ()
|
||||
Log.Debug $"Salt time: {s.Elapsed.TotalMilliseconds}"
|
||||
return salt
|
||||
}
|
||||
GetTemperature =
|
||||
fun aid t l ->
|
||||
async {
|
||||
let logData = {| aid = aid; t = t; l = l |}
|
||||
use _ = observer.trace ("GetTemperature", "{@log_data}", logData)
|
||||
return
|
||||
s.Start ()
|
||||
// let logData = {| aid = aid; t = t; l = l |}
|
||||
// use _ = observer.trace ("GetTemperature", "{@log_data}", logData)
|
||||
let temp =
|
||||
Fvcom.temp aid t l
|
||||
|> Result.defaultValue Array.empty
|
||||
s.Stop ()
|
||||
Log.Debug $"Temp time: {s.Elapsed.TotalMilliseconds}"
|
||||
return temp
|
||||
}
|
||||
GetZeta =
|
||||
fun aid t ->
|
||||
async {
|
||||
let logData = {| aid = aid; t = t |}
|
||||
use _ = observer.trace ("GetZeta", "{@log_data}", logData)
|
||||
return
|
||||
s.Start ()
|
||||
// let logData = {| aid = aid; t = t |}
|
||||
// use _ = observer.trace ("GetZeta", "{@log_data}", logData)
|
||||
let zeta =
|
||||
Fvcom.zeta aid t
|
||||
|> Result.defaultValue Array.empty
|
||||
s.Stop ()
|
||||
Log.Debug $"Zeta time: {s.Elapsed.TotalMilliseconds}"
|
||||
return zeta
|
||||
}
|
||||
GetSpeed =
|
||||
fun aid t l ->
|
||||
async {
|
||||
let logData = {| aid = aid; t = t; l = l |}
|
||||
use _ = observer.trace ("GetSpeed", "{@log_data}", logData)
|
||||
return
|
||||
s.Start ()
|
||||
// let logData = {| aid = aid; t = t; l = l |}
|
||||
// use _ = observer.trace ("GetSpeed", "{@log_data}", logData)
|
||||
let speed =
|
||||
Fvcom.speedAtNodes aid t l
|
||||
|> Result.defaultValue Array.empty
|
||||
s.Stop ()
|
||||
Log.Debug $"Speed time: {s.Elapsed.TotalMilliseconds}"
|
||||
return speed
|
||||
}
|
||||
GetUv =
|
||||
fun aid t l ->
|
||||
@@ -353,16 +368,117 @@ module Fvcom =
|
||||
GetUNode =
|
||||
fun aid t l ->
|
||||
async {
|
||||
return
|
||||
s.Start ()
|
||||
let u =
|
||||
Fvcom.uAtNodes aid t l
|
||||
|> Result.defaultValue Array.empty
|
||||
s.Stop ()
|
||||
Log.Debug $"U time: {s.Elapsed.TotalMilliseconds}"
|
||||
return u
|
||||
}
|
||||
GetVNode =
|
||||
fun aid t l ->
|
||||
async {
|
||||
return
|
||||
s.Start ()
|
||||
let v =
|
||||
Fvcom.vAtNodes aid t l
|
||||
|> Result.defaultValue Array.empty
|
||||
s.Stop ()
|
||||
Log.Debug $"V time: {s.Elapsed.TotalMilliseconds}"
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
let compressedLayerApi (ctx: HttpContext) : Fvcom.CompressedLayer =
|
||||
Log.Information("sorcerer: grid: user {username} -> {RequestPath}", ctx.User.Identity.Name, ctx.Request.Path)
|
||||
let s = Diagnostics.Stopwatch ()
|
||||
{
|
||||
GetBathymetry =
|
||||
fun aid ->
|
||||
async {
|
||||
return
|
||||
Settings.shaverArchives
|
||||
|> Map.tryFind aid
|
||||
|> Option.map (fun p -> IO.File.ReadAllBytes $"{p}/bathymetry.zfp")
|
||||
|> Option.defaultValue Array.empty
|
||||
}
|
||||
GetSalinity =
|
||||
fun aid t l ->
|
||||
async {
|
||||
s.Start ()
|
||||
let salt =
|
||||
Settings.shaverArchives
|
||||
|> Map.tryFind aid
|
||||
|> Option.map (fun p -> IO.File.ReadAllBytes $"{p}/salinity-{t}-{l}.zfp")
|
||||
|> Option.defaultValue Array.empty
|
||||
s.Stop ()
|
||||
Log.Debug $"Comp salt time: {s.Elapsed.TotalMilliseconds}"
|
||||
return salt
|
||||
}
|
||||
GetTemperature =
|
||||
fun aid t l ->
|
||||
async {
|
||||
s.Start ()
|
||||
let temp =
|
||||
Settings.shaverArchives
|
||||
|> Map.tryFind aid
|
||||
|> Option.map (fun p -> IO.File.ReadAllBytes $"{p}/temp-{t}-{l}.zfp")
|
||||
|> Option.defaultValue Array.empty
|
||||
s.Stop ()
|
||||
Log.Debug $"Comp temp time: {s.Elapsed.TotalMilliseconds}"
|
||||
return temp
|
||||
}
|
||||
GetZeta =
|
||||
fun aid t ->
|
||||
async {
|
||||
s.Start ()
|
||||
let zeta =
|
||||
Settings.shaverArchives
|
||||
|> Map.tryFind aid
|
||||
|> Option.map (fun p -> IO.File.ReadAllBytes $"{p}/zeta-{t}.zfp")
|
||||
|> Option.defaultValue Array.empty
|
||||
s.Stop ()
|
||||
Log.Debug $"Comp zeta time: {s.Elapsed.TotalMilliseconds}"
|
||||
return zeta
|
||||
}
|
||||
GetSpeed =
|
||||
fun aid t l ->
|
||||
async {
|
||||
s.Start ()
|
||||
let speed =
|
||||
Settings.shaverArchives
|
||||
|> Map.tryFind aid
|
||||
|> Option.map (fun p -> IO.File.ReadAllBytes $"{p}/speed-{t}-{l}.zfp")
|
||||
|> Option.defaultValue Array.empty
|
||||
s.Stop ()
|
||||
Log.Debug $"Comp speed time: {s.Elapsed.TotalMilliseconds}"
|
||||
return speed
|
||||
}
|
||||
GetUNode =
|
||||
fun aid t l ->
|
||||
async {
|
||||
s.Start ()
|
||||
let u =
|
||||
Settings.shaverArchives
|
||||
|> Map.tryFind aid
|
||||
|> Option.map (fun p -> IO.File.ReadAllBytes $"{p}/UNode-{t}-{l}.zfp")
|
||||
|> Option.defaultValue Array.empty
|
||||
s.Stop ()
|
||||
Log.Debug $"Comp U time: {s.Elapsed.TotalMilliseconds}"
|
||||
return u
|
||||
}
|
||||
GetVNode =
|
||||
fun aid t l ->
|
||||
async {
|
||||
s.Start ()
|
||||
let v =
|
||||
Settings.shaverArchives
|
||||
|> Map.tryFind aid
|
||||
|> Option.map (fun p -> IO.File.ReadAllBytes $"{p}/VNode-{t}-{l}.zfp")
|
||||
|> Option.defaultValue Array.empty
|
||||
s.Stop ()
|
||||
Log.Debug $"Comp V time: {s.Elapsed.TotalMilliseconds}"
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
@@ -845,6 +961,13 @@ module Endpoints =
|
||||
|> Remoting.withBinarySerialization
|
||||
|> Remoting.buildHttpHandler
|
||||
|
||||
let compressedLayerEndpoints: HttpHandler =
|
||||
Remoting.createApi ()
|
||||
|> Remoting.fromContext Fvcom.compressedLayerApi
|
||||
|> Remoting.withRouteBuilder Fvcom.routeBuilder
|
||||
|> Remoting.withBinarySerialization
|
||||
|> Remoting.buildHttpHandler
|
||||
|
||||
let nodeEndpoints: HttpHandler =
|
||||
Remoting.createApi ()
|
||||
|> Remoting.fromContext Fvcom.nodeApi
|
||||
|
||||
@@ -227,6 +227,7 @@ let webApp: HttpHandler =
|
||||
routeStartsWith "/api/v2" >=> requireViewPermission >=> choose [
|
||||
Fvcom.archiveEndpoints
|
||||
Fvcom.layerEndpoints
|
||||
Fvcom.compressedLayerEndpoints
|
||||
Fvcom.nodeEndpoints
|
||||
Fvcom.elementEndpoints
|
||||
Fvcom.batchEndpoints
|
||||
|
||||
@@ -272,6 +272,25 @@ let private archiveService: Archmaester.Dto.ArchiveService = {
|
||||
let archiveAgent = ArchiveAgent.ArchiveAgent(archiveService)
|
||||
let dataAgent = new DatasetAgent.DatasetAgent(archiveAgent)
|
||||
|
||||
// NOTE: Hacky hard-coded solution for now
|
||||
let shaverArchives : Map<Guid, string> =
|
||||
Map
|
||||
[
|
||||
Guid "0581b47d-89ac-46ae-ad41-c3cfbf02a371", "/data/archives/Oty/Napp-1"
|
||||
Guid "1954c96b-79d3-4f5b-8802-1b6020eca473", "/data/archives/Oty/Napp-2"
|
||||
Guid "799a83cb-8250-4dc1-a4da-6d6e507528d1", "/data/archives/Oty/Napp-3"
|
||||
Guid "80b3a73c-d0d7-4893-87b0-e41c513b95d8", "/data/archives/Oty/Napp-4"
|
||||
Guid "7c817a9e-2b02-467e-b8b0-16ea78675dfc", "/data/archives/Oty/Napp-5"
|
||||
Guid "4f6a655c-f79c-4911-9474-139ea1bc0596", "/data/archives/Oty/Napp-6"
|
||||
Guid "f7605499-12e2-46e0-a413-492f75218169", "/data/archives/Oty/Napp-7"
|
||||
Guid "11d9a61c-bd2c-4c4c-8943-1f2d1af91584", "/data/archives/Oty/Napp-8"
|
||||
Guid "4f5385b2-5164-475c-93cb-8c45ed27ddf4", "/data/archives/Oty/Napp-9"
|
||||
Guid "c518fe07-bf3c-49f4-9704-52d516d1f3bf", "/data/archives/Oty/Napp-10"
|
||||
Guid "c9849c87-eb7f-4c50-8add-b347e13b0bb5", "/data/archives/Oty/Napp-11"
|
||||
Guid "db88a44e-2d08-441c-826c-9a298b1dbb1e", "/data/archives/Oty/Napp-12"
|
||||
Guid "006e1823-1f1b-4b09-8769-4b63a3273515", "/data/archives/Oty/PO5"
|
||||
]
|
||||
|
||||
let barbsJson =
|
||||
let txt = File.ReadAllText "public/arrows.json"
|
||||
match Decode.Auto.fromString<Sorcerer.Types.Arrow<float> array> txt with
|
||||
|
||||
Reference in New Issue
Block a user