work
This commit is contained in:
parent
26a2212f70
commit
522e212d94
14 changed files with 157 additions and 316 deletions
36
.idea/workspace.xml
generated
36
.idea/workspace.xml
generated
|
@ -7,19 +7,21 @@
|
|||
<cargoProject FILE="$PROJECT_DIR$/Cargo.toml" />
|
||||
</component>
|
||||
<component name="ChangeListManager">
|
||||
<list default="true" id="2d855648-9644-469a-afa2-59beb52bb1d6" name="Changes" comment="feat: wxbox-ar2 round two">
|
||||
<list default="true" id="2d855648-9644-469a-afa2-59beb52bb1d6" name="Changes" comment="feat: optimizing wxbox-ar2">
|
||||
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/Cargo.lock" beforeDir="false" afterPath="$PROJECT_DIR$/Cargo.lock" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/crates/ar2/Cargo.toml" beforeDir="false" afterPath="$PROJECT_DIR$/crates/ar2/Cargo.toml" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/crates/ar2/benches/parse_benchmark.rs" beforeDir="false" afterPath="$PROJECT_DIR$/crates/ar2/benches/parse_benchmark.rs" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/crates/ar2/flamegraph.svg" beforeDir="false" afterPath="$PROJECT_DIR$/crates/ar2/flamegraph.svg" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/crates/ar2/src/lib.rs" beforeDir="false" afterPath="$PROJECT_DIR$/crates/ar2/src/lib.rs" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/client/src/lib/ToolbarProductSelector.svelte" beforeDir="false" afterPath="$PROJECT_DIR$/client/src/lib/ToolbarProductSelector.svelte" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/client/src/lib/generated_interop/digitalRadarData.ts" beforeDir="false" afterPath="$PROJECT_DIR$/client/src/lib/generated_interop/digitalRadarData.ts" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/client/src/lib/map/default_palettes.ts" beforeDir="false" afterPath="$PROJECT_DIR$/client/src/lib/map/default_palettes.ts" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/client/src/lib/map/fragment.glsl" beforeDir="false" afterPath="$PROJECT_DIR$/client/src/lib/map/fragment.glsl" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/client/src/lib/map/map.ts" beforeDir="false" afterPath="$PROJECT_DIR$/client/src/lib/map/map.ts" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/crates/ar2/src/main.rs" beforeDir="false" afterPath="$PROJECT_DIR$/crates/ar2/src/main.rs" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/crates/ar2/src/parse/error.rs" beforeDir="false" afterPath="$PROJECT_DIR$/crates/ar2/src/parse/error.rs" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/crates/ar2/src/parse/mod.rs" beforeDir="false" afterPath="$PROJECT_DIR$/crates/ar2/src/parse/mod.rs" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/crates/ar2/src/parse/scan.rs" beforeDir="false" afterPath="$PROJECT_DIR$/crates/ar2/src/parse/scan.rs" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/crates/ar2/src/parse/types.rs" beforeDir="false" afterPath="$PROJECT_DIR$/crates/ar2/src/parse/types.rs" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/crates/ar2/src/parse/util.rs" beforeDir="false" afterPath="$PROJECT_DIR$/crates/ar2/src/parse/util.rs" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/crates/interchange/src/ar2.rs" beforeDir="false" afterPath="$PROJECT_DIR$/crates/interchange/src/ar2.rs" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/crates/interchange/src/digitalRadarData.proto" beforeDir="false" afterPath="$PROJECT_DIR$/crates/interchange/src/digitalRadarData.proto" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/crates/pal/src/parser.rs" beforeDir="false" afterPath="$PROJECT_DIR$/crates/pal/src/parser.rs" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/crates/tiler/src/main.rs" beforeDir="false" afterPath="$PROJECT_DIR$/crates/tiler/src/main.rs" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/crates/tiler/src/nexrad.rs" beforeDir="false" afterPath="$PROJECT_DIR$/crates/tiler/src/nexrad.rs" afterDir="false" />
|
||||
</list>
|
||||
<option name="SHOW_DIALOG" value="false" />
|
||||
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
||||
|
@ -27,7 +29,7 @@
|
|||
<option name="LAST_RESOLUTION" value="IGNORE" />
|
||||
</component>
|
||||
<component name="DarkyenusTimeTracker">
|
||||
<option name="totalTimeSeconds" value="26794" />
|
||||
<option name="totalTimeSeconds" value="31634" />
|
||||
</component>
|
||||
<component name="FileTemplateManagerImpl">
|
||||
<option name="RECENT_TEMPLATES">
|
||||
|
@ -148,6 +150,7 @@
|
|||
<workItem from="1748132890039" duration="1214000" />
|
||||
<workItem from="1748634364820" duration="2604000" />
|
||||
<workItem from="1748807470356" duration="7770000" />
|
||||
<workItem from="1749250715883" duration="365000" />
|
||||
</task>
|
||||
<task id="LOCAL-00001" summary="debugging">
|
||||
<option name="closed" value="true" />
|
||||
|
@ -245,7 +248,15 @@
|
|||
<option name="project" value="LOCAL" />
|
||||
<updated>1749001700392</updated>
|
||||
</task>
|
||||
<option name="localTasksCounter" value="13" />
|
||||
<task id="LOCAL-00013" summary="feat: optimizing wxbox-ar2">
|
||||
<option name="closed" value="true" />
|
||||
<created>1749006685171</created>
|
||||
<option name="number" value="00013" />
|
||||
<option name="presentableId" value="LOCAL-00013" />
|
||||
<option name="project" value="LOCAL" />
|
||||
<updated>1749006685171</updated>
|
||||
</task>
|
||||
<option name="localTasksCounter" value="14" />
|
||||
<servers />
|
||||
</component>
|
||||
<component name="TypeScriptGeneratedFilesManager">
|
||||
|
@ -264,6 +275,7 @@
|
|||
<MESSAGE value="feat: new ui" />
|
||||
<MESSAGE value="bug: try to fix some multimap weirdness" />
|
||||
<MESSAGE value="feat: wxbox-ar2 round two" />
|
||||
<option name="LAST_COMMIT_MESSAGE" value="feat: wxbox-ar2 round two" />
|
||||
<MESSAGE value="feat: optimizing wxbox-ar2" />
|
||||
<option name="LAST_COMMIT_MESSAGE" value="feat: optimizing wxbox-ar2" />
|
||||
</component>
|
||||
</project>
|
|
@ -49,7 +49,7 @@
|
|||
if (data.product == "REF") {
|
||||
palette = WXBOX_STANDARD_REF;
|
||||
} else if (data.product == "VEL") {
|
||||
palette = WXBOX_STANDARD_REF;
|
||||
palette = WXBOX_STANDARD_VEL;
|
||||
} else if (data.product == "SW") {
|
||||
palette = BENS_SW;
|
||||
} else if (data.product == "ZDR") {
|
||||
|
@ -117,7 +117,7 @@
|
|||
if (map.getLayer("data")) {
|
||||
map.removeLayer('data');
|
||||
}
|
||||
plotRadial(map, selectedSite, thisProduct, 1, stationLat, stationLong, thisPalette);
|
||||
plotRadial(map, selectedSite, thisProduct,1, stationLat, stationLong, thisPalette);
|
||||
} // preserve the layer the user had if they click while already selected.
|
||||
}
|
||||
});
|
||||
|
|
|
@ -14,10 +14,6 @@ import { MessageType } from "@protobuf-ts/runtime";
|
|||
* @generated from protobuf message cif.digitalRadarData.DigitalRadarData
|
||||
*/
|
||||
export interface DigitalRadarData {
|
||||
/**
|
||||
* @generated from protobuf field: int32 vcpNumber = 1
|
||||
*/
|
||||
vcpNumber: number;
|
||||
/**
|
||||
* @generated from protobuf field: int32 elevationNumber = 2
|
||||
*/
|
||||
|
@ -31,10 +27,6 @@ export interface DigitalRadarData {
|
|||
* @generated from protobuf message cif.digitalRadarData.Radial
|
||||
*/
|
||||
export interface Radial {
|
||||
/**
|
||||
* @generated from protobuf field: fixed64 collectionTimestamp = 1
|
||||
*/
|
||||
collectionTimestamp: bigint;
|
||||
/**
|
||||
* @generated from protobuf field: int32 azimuthNumber = 2
|
||||
*/
|
||||
|
@ -43,14 +35,6 @@ export interface Radial {
|
|||
* @generated from protobuf field: float azimuthAngleDegrees = 3
|
||||
*/
|
||||
azimuthAngleDegrees: number;
|
||||
/**
|
||||
* @generated from protobuf field: float azimuthSpacingDegrees = 4
|
||||
*/
|
||||
azimuthSpacingDegrees: number;
|
||||
/**
|
||||
* @generated from protobuf field: cif.digitalRadarData.RadialStatus radialStatus = 5
|
||||
*/
|
||||
radialStatus: RadialStatus;
|
||||
/**
|
||||
* @generated from protobuf field: int32 elevationNumber = 6
|
||||
*/
|
||||
|
@ -90,14 +74,6 @@ export interface MomentaryMeta {
|
|||
* @generated from protobuf message cif.digitalRadarData.MomentaryData
|
||||
*/
|
||||
export interface MomentaryData {
|
||||
/**
|
||||
* @generated from protobuf field: float scale = 1
|
||||
*/
|
||||
scale: number;
|
||||
/**
|
||||
* @generated from protobuf field: float offset = 2
|
||||
*/
|
||||
offset: number;
|
||||
/**
|
||||
* @generated from protobuf field: int32 startRange = 3
|
||||
*/
|
||||
|
@ -107,55 +83,20 @@ export interface MomentaryData {
|
|||
*/
|
||||
sampleInterval: number;
|
||||
/**
|
||||
* @generated from protobuf field: repeated int32 data = 5
|
||||
* @generated from protobuf field: repeated double data = 6
|
||||
*/
|
||||
data: number[];
|
||||
}
|
||||
/**
|
||||
* @generated from protobuf enum cif.digitalRadarData.RadialStatus
|
||||
*/
|
||||
export enum RadialStatus {
|
||||
/**
|
||||
* @generated from protobuf enum value: RADIAL_STATUS_UNKNOWN = 0;
|
||||
*/
|
||||
UNKNOWN = 0,
|
||||
/**
|
||||
* @generated from protobuf enum value: RADIAL_STATUS_ELEVATION_START = 1;
|
||||
*/
|
||||
ELEVATION_START = 1,
|
||||
/**
|
||||
* @generated from protobuf enum value: RADIAL_STATUS_INTERMEDIATE_RADIAL_DATA = 2;
|
||||
*/
|
||||
INTERMEDIATE_RADIAL_DATA = 2,
|
||||
/**
|
||||
* @generated from protobuf enum value: RADIAL_STATUS_ELEVATION_END = 3;
|
||||
*/
|
||||
ELEVATION_END = 3,
|
||||
/**
|
||||
* @generated from protobuf enum value: RADIAL_STATUS_VOLUME_SCAN_START = 4;
|
||||
*/
|
||||
VOLUME_SCAN_START = 4,
|
||||
/**
|
||||
* @generated from protobuf enum value: RADIAL_STATUS_VOLUME_SCAN_END = 5;
|
||||
*/
|
||||
VOLUME_SCAN_END = 5,
|
||||
/**
|
||||
* @generated from protobuf enum value: RADIAL_STATUS_ELEVATION_START_VCP_FINAL = 6;
|
||||
*/
|
||||
ELEVATION_START_VCP_FINAL = 6
|
||||
}
|
||||
// @generated message type with reflection information, may provide speed optimized methods
|
||||
class DigitalRadarData$Type extends MessageType<DigitalRadarData> {
|
||||
constructor() {
|
||||
super("cif.digitalRadarData.DigitalRadarData", [
|
||||
{ no: 1, name: "vcpNumber", kind: "scalar", T: 5 /*ScalarType.INT32*/ },
|
||||
{ no: 2, name: "elevationNumber", kind: "scalar", T: 5 /*ScalarType.INT32*/ },
|
||||
{ no: 3, name: "radials", kind: "message", repeat: 2 /*RepeatType.UNPACKED*/, T: () => Radial }
|
||||
]);
|
||||
}
|
||||
create(value?: PartialMessage<DigitalRadarData>): DigitalRadarData {
|
||||
const message = globalThis.Object.create((this.messagePrototype!));
|
||||
message.vcpNumber = 0;
|
||||
message.elevationNumber = 0;
|
||||
message.radials = [];
|
||||
if (value !== undefined)
|
||||
|
@ -167,9 +108,6 @@ class DigitalRadarData$Type extends MessageType<DigitalRadarData> {
|
|||
while (reader.pos < end) {
|
||||
let [fieldNo, wireType] = reader.tag();
|
||||
switch (fieldNo) {
|
||||
case /* int32 vcpNumber */ 1:
|
||||
message.vcpNumber = reader.int32();
|
||||
break;
|
||||
case /* int32 elevationNumber */ 2:
|
||||
message.elevationNumber = reader.int32();
|
||||
break;
|
||||
|
@ -188,9 +126,6 @@ class DigitalRadarData$Type extends MessageType<DigitalRadarData> {
|
|||
return message;
|
||||
}
|
||||
internalBinaryWrite(message: DigitalRadarData, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter {
|
||||
/* int32 vcpNumber = 1; */
|
||||
if (message.vcpNumber !== 0)
|
||||
writer.tag(1, WireType.Varint).int32(message.vcpNumber);
|
||||
/* int32 elevationNumber = 2; */
|
||||
if (message.elevationNumber !== 0)
|
||||
writer.tag(2, WireType.Varint).int32(message.elevationNumber);
|
||||
|
@ -211,11 +146,8 @@ export const DigitalRadarData = new DigitalRadarData$Type();
|
|||
class Radial$Type extends MessageType<Radial> {
|
||||
constructor() {
|
||||
super("cif.digitalRadarData.Radial", [
|
||||
{ no: 1, name: "collectionTimestamp", kind: "scalar", T: 6 /*ScalarType.FIXED64*/, L: 0 /*LongType.BIGINT*/ },
|
||||
{ no: 2, name: "azimuthNumber", kind: "scalar", T: 5 /*ScalarType.INT32*/ },
|
||||
{ no: 3, name: "azimuthAngleDegrees", kind: "scalar", T: 2 /*ScalarType.FLOAT*/ },
|
||||
{ no: 4, name: "azimuthSpacingDegrees", kind: "scalar", T: 2 /*ScalarType.FLOAT*/ },
|
||||
{ no: 5, name: "radialStatus", kind: "enum", T: () => ["cif.digitalRadarData.RadialStatus", RadialStatus, "RADIAL_STATUS_"] },
|
||||
{ no: 6, name: "elevationNumber", kind: "scalar", T: 5 /*ScalarType.INT32*/ },
|
||||
{ no: 7, name: "elevationDegrees", kind: "scalar", T: 2 /*ScalarType.FLOAT*/ },
|
||||
{ no: 8, name: "product", kind: "message", T: () => MomentaryProduct }
|
||||
|
@ -223,11 +155,8 @@ class Radial$Type extends MessageType<Radial> {
|
|||
}
|
||||
create(value?: PartialMessage<Radial>): Radial {
|
||||
const message = globalThis.Object.create((this.messagePrototype!));
|
||||
message.collectionTimestamp = 0n;
|
||||
message.azimuthNumber = 0;
|
||||
message.azimuthAngleDegrees = 0;
|
||||
message.azimuthSpacingDegrees = 0;
|
||||
message.radialStatus = 0;
|
||||
message.elevationNumber = 0;
|
||||
message.elevationDegrees = 0;
|
||||
if (value !== undefined)
|
||||
|
@ -239,21 +168,12 @@ class Radial$Type extends MessageType<Radial> {
|
|||
while (reader.pos < end) {
|
||||
let [fieldNo, wireType] = reader.tag();
|
||||
switch (fieldNo) {
|
||||
case /* fixed64 collectionTimestamp */ 1:
|
||||
message.collectionTimestamp = reader.fixed64().toBigInt();
|
||||
break;
|
||||
case /* int32 azimuthNumber */ 2:
|
||||
message.azimuthNumber = reader.int32();
|
||||
break;
|
||||
case /* float azimuthAngleDegrees */ 3:
|
||||
message.azimuthAngleDegrees = reader.float();
|
||||
break;
|
||||
case /* float azimuthSpacingDegrees */ 4:
|
||||
message.azimuthSpacingDegrees = reader.float();
|
||||
break;
|
||||
case /* cif.digitalRadarData.RadialStatus radialStatus */ 5:
|
||||
message.radialStatus = reader.int32();
|
||||
break;
|
||||
case /* int32 elevationNumber */ 6:
|
||||
message.elevationNumber = reader.int32();
|
||||
break;
|
||||
|
@ -275,21 +195,12 @@ class Radial$Type extends MessageType<Radial> {
|
|||
return message;
|
||||
}
|
||||
internalBinaryWrite(message: Radial, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter {
|
||||
/* fixed64 collectionTimestamp = 1; */
|
||||
if (message.collectionTimestamp !== 0n)
|
||||
writer.tag(1, WireType.Bit64).fixed64(message.collectionTimestamp);
|
||||
/* int32 azimuthNumber = 2; */
|
||||
if (message.azimuthNumber !== 0)
|
||||
writer.tag(2, WireType.Varint).int32(message.azimuthNumber);
|
||||
/* float azimuthAngleDegrees = 3; */
|
||||
if (message.azimuthAngleDegrees !== 0)
|
||||
writer.tag(3, WireType.Bit32).float(message.azimuthAngleDegrees);
|
||||
/* float azimuthSpacingDegrees = 4; */
|
||||
if (message.azimuthSpacingDegrees !== 0)
|
||||
writer.tag(4, WireType.Bit32).float(message.azimuthSpacingDegrees);
|
||||
/* cif.digitalRadarData.RadialStatus radialStatus = 5; */
|
||||
if (message.radialStatus !== 0)
|
||||
writer.tag(5, WireType.Varint).int32(message.radialStatus);
|
||||
/* int32 elevationNumber = 6; */
|
||||
if (message.elevationNumber !== 0)
|
||||
writer.tag(6, WireType.Varint).int32(message.elevationNumber);
|
||||
|
@ -413,17 +324,13 @@ export const MomentaryMeta = new MomentaryMeta$Type();
|
|||
class MomentaryData$Type extends MessageType<MomentaryData> {
|
||||
constructor() {
|
||||
super("cif.digitalRadarData.MomentaryData", [
|
||||
{ no: 1, name: "scale", kind: "scalar", T: 2 /*ScalarType.FLOAT*/ },
|
||||
{ no: 2, name: "offset", kind: "scalar", T: 2 /*ScalarType.FLOAT*/ },
|
||||
{ no: 3, name: "startRange", kind: "scalar", T: 5 /*ScalarType.INT32*/ },
|
||||
{ no: 4, name: "sampleInterval", kind: "scalar", T: 5 /*ScalarType.INT32*/ },
|
||||
{ no: 5, name: "data", kind: "scalar", repeat: 1 /*RepeatType.PACKED*/, T: 5 /*ScalarType.INT32*/ }
|
||||
{ no: 6, name: "data", kind: "scalar", repeat: 1 /*RepeatType.PACKED*/, T: 1 /*ScalarType.DOUBLE*/ }
|
||||
]);
|
||||
}
|
||||
create(value?: PartialMessage<MomentaryData>): MomentaryData {
|
||||
const message = globalThis.Object.create((this.messagePrototype!));
|
||||
message.scale = 0;
|
||||
message.offset = 0;
|
||||
message.startRange = 0;
|
||||
message.sampleInterval = 0;
|
||||
message.data = [];
|
||||
|
@ -436,24 +343,18 @@ class MomentaryData$Type extends MessageType<MomentaryData> {
|
|||
while (reader.pos < end) {
|
||||
let [fieldNo, wireType] = reader.tag();
|
||||
switch (fieldNo) {
|
||||
case /* float scale */ 1:
|
||||
message.scale = reader.float();
|
||||
break;
|
||||
case /* float offset */ 2:
|
||||
message.offset = reader.float();
|
||||
break;
|
||||
case /* int32 startRange */ 3:
|
||||
message.startRange = reader.int32();
|
||||
break;
|
||||
case /* int32 sampleInterval */ 4:
|
||||
message.sampleInterval = reader.int32();
|
||||
break;
|
||||
case /* repeated int32 data */ 5:
|
||||
case /* repeated double data */ 6:
|
||||
if (wireType === WireType.LengthDelimited)
|
||||
for (let e = reader.int32() + reader.pos; reader.pos < e;)
|
||||
message.data.push(reader.int32());
|
||||
message.data.push(reader.double());
|
||||
else
|
||||
message.data.push(reader.int32());
|
||||
message.data.push(reader.double());
|
||||
break;
|
||||
default:
|
||||
let u = options.readUnknownField;
|
||||
|
@ -467,23 +368,17 @@ class MomentaryData$Type extends MessageType<MomentaryData> {
|
|||
return message;
|
||||
}
|
||||
internalBinaryWrite(message: MomentaryData, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter {
|
||||
/* float scale = 1; */
|
||||
if (message.scale !== 0)
|
||||
writer.tag(1, WireType.Bit32).float(message.scale);
|
||||
/* float offset = 2; */
|
||||
if (message.offset !== 0)
|
||||
writer.tag(2, WireType.Bit32).float(message.offset);
|
||||
/* int32 startRange = 3; */
|
||||
if (message.startRange !== 0)
|
||||
writer.tag(3, WireType.Varint).int32(message.startRange);
|
||||
/* int32 sampleInterval = 4; */
|
||||
if (message.sampleInterval !== 0)
|
||||
writer.tag(4, WireType.Varint).int32(message.sampleInterval);
|
||||
/* repeated int32 data = 5; */
|
||||
/* repeated double data = 6; */
|
||||
if (message.data.length) {
|
||||
writer.tag(5, WireType.LengthDelimited).fork();
|
||||
writer.tag(6, WireType.LengthDelimited).fork();
|
||||
for (let i = 0; i < message.data.length; i++)
|
||||
writer.int32(message.data[i]);
|
||||
writer.double(message.data[i]);
|
||||
writer.join();
|
||||
}
|
||||
let u = options.writeUnknownFields;
|
||||
|
|
|
@ -7,18 +7,26 @@ Color4: 60 255 0 255 255 128 0 128 255
|
|||
Color4: 70 255 255 255 255 128 128 128 255
|
||||
Color4: 80 128 128 128 255`;
|
||||
|
||||
export const WXBOX_STANDARD_VEL = `color: 0 128 128 128
|
||||
color: -10 0 81 0
|
||||
color: -30 0 169 0
|
||||
color: -50 105 253 103
|
||||
color: -60 220 215 252
|
||||
color: -120 120 120 253
|
||||
color: 10 99 0 0
|
||||
color: 40 239 7 0
|
||||
color: 45 255 88 1
|
||||
color: 55 255 181 1
|
||||
color: 100 255 255 0
|
||||
color: 120 255 255 255`;
|
||||
export const WXBOX_STANDARD_VEL = `Units: KTS
|
||||
Step: 10
|
||||
Product: BV
|
||||
Scale: 1.942
|
||||
Color: 120 255 230 170
|
||||
Color: 100 255 255 255
|
||||
Color: 90 250 191 238
|
||||
Color: 65 254 39 201
|
||||
Color: 45 100 0 0
|
||||
Color: 20 234 41 55
|
||||
Color: 10 255 111 96
|
||||
Color: 5 245 154 154
|
||||
Color: 0 253 245 219
|
||||
Color: -5 99 255 141
|
||||
Color: -20 0 170 0
|
||||
Color: -45 0 70 0
|
||||
Color: -60 18 235 255
|
||||
Color: -70 8 140 255
|
||||
Color: -130 98 27 191
|
||||
RF: 96 2 97`;
|
||||
|
||||
export const BENS_SW = `$$Ben Mitchell
|
||||
Product: SW
|
||||
|
|
|
@ -49,7 +49,10 @@ void main() {
|
|||
|
||||
float start_distance = 2125.0;
|
||||
float interval = 250.0;
|
||||
int number_of_gates = 1832;
|
||||
|
||||
ivec2 data_size = textureSize(data, 0);
|
||||
|
||||
int number_of_gates = data_size.y;
|
||||
|
||||
if (distance_meters < start_distance) {
|
||||
fragColor = vec4(0,0,0,0);
|
||||
|
@ -63,12 +66,21 @@ void main() {
|
|||
int gate_number = int(floor((distance_meters - 2125.0) / 250.0));
|
||||
float rawValue = texelFetch(data, ivec2(gate_number, azimuthNumber), 0).r;
|
||||
|
||||
if (abs(rawValue - (-999.0)) < 0.01) {
|
||||
fragColor = vec4(1,0,0,1);
|
||||
return; // below threshold
|
||||
}
|
||||
if (abs(rawValue - (-9999.0)) < 0.01) {
|
||||
fragColor = vec4(96.0/255.0,2.0/255.0,97.0/255.0,1);
|
||||
return; // range folded
|
||||
}
|
||||
|
||||
// colorize!
|
||||
for (int i = paletteLength; i > 0; i--) {
|
||||
vec2 rangeBounds = texelFetch(bounds, ivec2(0, i), 0).rg;
|
||||
float lower = rangeBounds.r;
|
||||
float higher = rangeBounds.g;
|
||||
if (rawValue >= lower && rawValue < higher) {
|
||||
if ((rawValue >= lower && rawValue < higher) || (rawValue >= higher && rawValue < lower)) {
|
||||
float mapped_end = higher - lower;
|
||||
float mapped_point = rawValue - lower;
|
||||
float t = mapped_point / mapped_end;
|
||||
|
@ -76,13 +88,14 @@ void main() {
|
|||
vec4 color_start = texelFetch(colorData, ivec2(0, i), 0);
|
||||
vec4 color_end = texelFetch(color2Data, ivec2(0, i), 0);
|
||||
|
||||
|
||||
|
||||
fragColor = mix(color_start, color_end, t);
|
||||
if (fragColor == vec4(0,0,0,0)) {
|
||||
fragColor = vec4(0,1,0,1);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
fragColor = vec4(0,0,0,0);
|
||||
fragColor = vec4(0,0,0,1);
|
||||
return;
|
||||
}
|
|
@ -113,37 +113,25 @@ export async function plotRadial(map: Map, site: string, product: string, elevat
|
|||
this.vertexCount = vertexData.length/2;
|
||||
gl.uniform1fv(gl.getUniformLocation(this.program, 'triangleAzimuthLookup'), new Float32Array(triangleAzimuthLookup));
|
||||
|
||||
function scaleMomentData(radial: Radial, u: number): number {
|
||||
if (!radial.product || !radial.product.data) return BELOW_THRESHOLD;
|
||||
if (radial.product?.data?.scale == 0) {
|
||||
return u;
|
||||
} else {
|
||||
if (u == 0) {
|
||||
return BELOW_THRESHOLD;
|
||||
} else if (u == 1) {
|
||||
return RANGE_FOLDED;
|
||||
} else {
|
||||
return (u - radial.product.data?.offset) / radial.product?.data?.scale;
|
||||
}
|
||||
}
|
||||
}
|
||||
const raw_data = [];
|
||||
let validRadials = 0;
|
||||
|
||||
for (const radial of drd.radials) {
|
||||
if (radial.product && radial.product.data && radial.product.data.data) {
|
||||
for (const datapoint of radial.product.data.data) {
|
||||
raw_data.push(scaleMomentData(radial, datapoint));
|
||||
raw_data.push(datapoint);
|
||||
}
|
||||
validRadials++;
|
||||
}
|
||||
}
|
||||
|
||||
const data = new Float32Array(raw_data);
|
||||
console.log(data);
|
||||
console.log(data.length / validRadials);
|
||||
gl.activeTexture(gl.TEXTURE0 + 10);
|
||||
this.texture = gl.createTexture();
|
||||
gl.bindTexture(gl.TEXTURE_2D, this.texture);
|
||||
gl.texImage2D(gl.TEXTURE_2D, 0, gl.R32F, 1832, validRadials, 0, gl.RED, gl.FLOAT, data);
|
||||
gl.texImage2D(gl.TEXTURE_2D, 0, gl.R32F, data.length / validRadials, validRadials, 0, gl.RED, gl.FLOAT, data);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
|
||||
|
|
|
@ -11,5 +11,12 @@ fn main() {
|
|||
.init();
|
||||
|
||||
let mut f = File::open(env::args().nth(1).unwrap()).unwrap();
|
||||
println!("{:?}", Ar2v::read(&mut f));
|
||||
let ar = Ar2v::read(&mut f).unwrap();
|
||||
for elev in &ar.scan.elevations {
|
||||
for radial in &elev.1.radials {
|
||||
if radial.1.vel.is_some() {
|
||||
println!("VEL! e{}r{}", elev.0, radial.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,8 +21,8 @@ pub mod util;
|
|||
const DEFAULT_MESSAGE_SIZE: usize = 2432;
|
||||
const MESSAGE_BODY_SIZE: usize = DEFAULT_MESSAGE_SIZE - 12 - 16;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Ar2v {
|
||||
pub scan: Scan,
|
||||
pub volume_header: VolumeHeaderRecord,
|
||||
}
|
||||
impl Ar2v {
|
||||
|
@ -232,26 +232,26 @@ impl Ar2v {
|
|||
}
|
||||
|
||||
// scale the data
|
||||
let chunks = data.1.chunks(data.0.data_word_size as usize / 8);
|
||||
|
||||
let mp = MomentaryProduct {
|
||||
start_range_meters: data.0.data_moment_range as isize,
|
||||
data_spacing_meters: data.0.data_moment_range_sample_interval as isize,
|
||||
data: chunks
|
||||
data: if data.0.data_word_size == 16 {
|
||||
data.1.chunks(2).map(|u| u16::from_be_bytes(u.try_into().unwrap()) as usize).collect::<Vec<_>>()
|
||||
} else {
|
||||
data.1.iter().map(|u| *u as usize).collect::<Vec<_>>()
|
||||
}
|
||||
.iter()
|
||||
.map(|u| {
|
||||
let mut result = [0u8; 8];
|
||||
result[..u.len()].copy_from_slice(u);
|
||||
usize::from_be_bytes(result)
|
||||
})
|
||||
.map(|u| {
|
||||
if u == 0 {
|
||||
if *u == 0 {
|
||||
Gate::BelowThreshold
|
||||
} else if u == 1 {
|
||||
} else if *u == 1 {
|
||||
Gate::RangeFolded
|
||||
} else if data.0.scale == 0.0 {
|
||||
Gate::Data(u as f64)
|
||||
Gate::Data(*u as f64)
|
||||
} else {
|
||||
Gate::Data(
|
||||
(u as f64) - data.0.offset as f64 / data.0.scale as f64,
|
||||
((*u as f64) - data.0.offset as f64) / data.0.scale as f64,
|
||||
)
|
||||
}
|
||||
})
|
||||
|
@ -302,7 +302,7 @@ impl Ar2v {
|
|||
};
|
||||
}
|
||||
|
||||
Ok(Self { volume_header: vhr })
|
||||
Ok(Self { volume_header: vhr, scan })
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,14 +1,17 @@
|
|||
use std::collections::BTreeMap;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Scan {
|
||||
pub elevations: BTreeMap<usize, Elevation>,
|
||||
}
|
||||
#[derive(Clone)]
|
||||
pub struct Elevation {
|
||||
pub elevation_number: usize,
|
||||
pub elevation_angle: f64,
|
||||
|
||||
pub radials: BTreeMap<usize, Radial>,
|
||||
}
|
||||
#[derive(Clone)]
|
||||
pub struct Radial {
|
||||
pub radial_number: usize,
|
||||
pub azimuth_angle: f64,
|
||||
|
@ -21,11 +24,13 @@ pub struct Radial {
|
|||
pub phi: Option<MomentaryProduct>,
|
||||
pub rho: Option<MomentaryProduct>,
|
||||
}
|
||||
#[derive(Clone)]
|
||||
pub struct MomentaryProduct {
|
||||
pub start_range_meters: isize,
|
||||
pub data_spacing_meters: isize,
|
||||
pub data: Vec<Gate>,
|
||||
}
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum Gate {
|
||||
BelowThreshold,
|
||||
RangeFolded,
|
||||
|
|
|
@ -2,58 +2,59 @@ use ordered_float::OrderedFloat;
|
|||
use crate::cif::cif_container::MessageType;
|
||||
use crate::{AsCif, cif};
|
||||
use nexrad_decode::messages::digital_radar_data::RadialStatus;
|
||||
use wxbox_ar2::{MomentData, Radial, Scan};
|
||||
use wxbox_ar2::parse::Ar2v;
|
||||
use wxbox_ar2::parse::scan::{Gate, MomentaryProduct, Radial};
|
||||
|
||||
pub struct Ar2AsCifParams {
|
||||
pub requested_elevation: u8,
|
||||
pub requested_product: String,
|
||||
}
|
||||
|
||||
impl AsCif for Scan {
|
||||
impl AsCif for Ar2v {
|
||||
type Params = Ar2AsCifParams;
|
||||
fn as_cif(&self, params: &Self::Params) -> cif::CifContainer {
|
||||
let mut digital_radar_data = cif::digital_radar_data::DigitalRadarData::default();
|
||||
digital_radar_data.elevation_number = params.requested_elevation as i32;
|
||||
digital_radar_data.vcp_number = self.coverage_pattern_number as i32;
|
||||
|
||||
// find the elevation
|
||||
let mut maybe_elevation = self
|
||||
.sweeps
|
||||
.iter()
|
||||
.find(|u| u.elevation_number == params.requested_elevation);
|
||||
.scan.elevations
|
||||
.get(&(params.requested_elevation as usize));
|
||||
if let Some(elevation) = maybe_elevation {
|
||||
// parse out the radials
|
||||
let mut radials_we_can_use: Vec<(&Radial, &MomentData)> = vec![];
|
||||
let mut radials_we_can_use: Vec<(&Radial, &MomentaryProduct)> = vec![];
|
||||
let mut elevation = elevation.clone();
|
||||
elevation.radials.sort_by_key(|u| OrderedFloat(u.azimuth_angle_degrees));
|
||||
for radial in &elevation.radials {
|
||||
|
||||
radials_we_can_use.sort_by_key(|u| OrderedFloat(u.0.azimuth_angle));
|
||||
|
||||
for (radial_num, radial) in &elevation.radials {
|
||||
match params.requested_product.as_str() {
|
||||
"REF" if radial.reflectivity.is_some() => {
|
||||
radials_we_can_use.push((&radial, &radial.reflectivity.as_ref().unwrap()));
|
||||
"REF" if radial.refl.is_some() => {
|
||||
radials_we_can_use.push((&radial, &radial.refl.as_ref().unwrap()));
|
||||
}
|
||||
"VEL" if radial.velocity.is_some() => {
|
||||
radials_we_can_use.push((&radial, &radial.velocity.as_ref().unwrap()));
|
||||
"VEL" if radial.vel.is_some() => {
|
||||
radials_we_can_use.push((&radial, &radial.vel.as_ref().unwrap()));
|
||||
}
|
||||
"SW" if radial.spectrum_width.is_some() => {
|
||||
"SW" if radial.sw.is_some() => {
|
||||
radials_we_can_use
|
||||
.push((&radial, &radial.spectrum_width.as_ref().unwrap()));
|
||||
.push((&radial, &radial.sw.as_ref().unwrap()));
|
||||
}
|
||||
"ZDR" if radial.differential_reflectivity.is_some() => {
|
||||
"ZDR" if radial.zdr.is_some() => {
|
||||
radials_we_can_use
|
||||
.push((&radial, &radial.differential_reflectivity.as_ref().unwrap()));
|
||||
.push((&radial, &radial.zdr.as_ref().unwrap()));
|
||||
}
|
||||
"PHI" if radial.differential_phase.is_some() => {
|
||||
"PHI" if radial.phi.is_some() => {
|
||||
radials_we_can_use
|
||||
.push((&radial, &radial.differential_phase.as_ref().unwrap()));
|
||||
.push((&radial, &radial.phi.as_ref().unwrap()));
|
||||
}
|
||||
"RHO" if radial.correlation_coefficient.is_some() => {
|
||||
"RHO" if radial.rho.is_some() => {
|
||||
radials_we_can_use
|
||||
.push((&radial, &radial.correlation_coefficient.as_ref().unwrap()));
|
||||
.push((&radial, &radial.rho.as_ref().unwrap()));
|
||||
}
|
||||
"CFP" if radial.specific_differential_phase.is_some() => {
|
||||
"CFP" if radial.cfp.is_some() => {
|
||||
radials_we_can_use.push((
|
||||
&radial,
|
||||
&radial.specific_differential_phase.as_ref().unwrap(),
|
||||
&radial.cfp.as_ref().unwrap(),
|
||||
));
|
||||
}
|
||||
_ => {}
|
||||
|
@ -64,43 +65,22 @@ impl AsCif for Scan {
|
|||
.iter()
|
||||
.map(|u| {
|
||||
let mut radial = cif::digital_radar_data::Radial::default();
|
||||
radial.elevation_number = u.0.elevation_number as i32;
|
||||
radial.azimuth_angle_degrees = u.0.azimuth_angle_degrees;
|
||||
radial.azimuth_number = u.0.azimuth_number as i32;
|
||||
radial.azimuth_spacing_degrees = u.0.azimuth_spacing_degrees;
|
||||
radial.collection_timestamp = u.0.collection_timestamp as u64;
|
||||
radial.elevation_degrees = u.0.elevation_number_degrees;
|
||||
radial.radial_status = match u.0.radial_status {
|
||||
RadialStatus::ElevationStart => {
|
||||
cif::digital_radar_data::RadialStatus::ElevationStart
|
||||
}
|
||||
RadialStatus::IntermediateRadialData => {
|
||||
cif::digital_radar_data::RadialStatus::IntermediateRadialData
|
||||
}
|
||||
RadialStatus::ElevationEnd => {
|
||||
cif::digital_radar_data::RadialStatus::ElevationEnd
|
||||
}
|
||||
RadialStatus::VolumeScanStart => {
|
||||
cif::digital_radar_data::RadialStatus::VolumeScanStart
|
||||
}
|
||||
RadialStatus::VolumeScanEnd => {
|
||||
cif::digital_radar_data::RadialStatus::VolumeScanEnd
|
||||
}
|
||||
RadialStatus::ElevationStartVCPFinal => {
|
||||
cif::digital_radar_data::RadialStatus::ElevationStartVcpFinal
|
||||
}
|
||||
}
|
||||
.into();
|
||||
radial.elevation_number = elevation.elevation_number as i32;
|
||||
radial.azimuth_angle_degrees = u.0.azimuth_angle as f32;
|
||||
radial.azimuth_number = u.0.radial_number as i32;
|
||||
radial.elevation_degrees = elevation.elevation_angle as f32;
|
||||
|
||||
let mut moment_meta = cif::digital_radar_data::MomentaryMeta::default();
|
||||
moment_meta.product_name = params.requested_product.clone();
|
||||
|
||||
let mut moment_data = cif::digital_radar_data::MomentaryData::default();
|
||||
moment_data.offset = u.1.offset;
|
||||
moment_data.sample_interval = u.1.sample_interval as i32;
|
||||
moment_data.scale = u.1.scale;
|
||||
moment_data.start_range = u.1.start_range as i32;
|
||||
moment_data.data = u.1.values.iter().map(|u| *u as i32).collect();
|
||||
moment_data.sample_interval = u.1.data_spacing_meters as i32;
|
||||
moment_data.start_range = u.1.start_range_meters as i32;
|
||||
moment_data.data = u.1.data.iter().map(|u| match u {
|
||||
Gate::BelowThreshold => -999.0_f64,
|
||||
Gate::RangeFolded => -9999.0_f64,
|
||||
Gate::Data(d) => *d
|
||||
}).collect();
|
||||
|
||||
let mut moment_product = cif::digital_radar_data::MomentaryProduct::default();
|
||||
moment_product.product_metadata = Some(moment_meta);
|
||||
|
|
|
@ -3,20 +3,18 @@ syntax = "proto3";
|
|||
package cif.digitalRadarData;
|
||||
|
||||
message DigitalRadarData {
|
||||
int32 vcpNumber = 1;
|
||||
reserved 1;
|
||||
|
||||
int32 elevationNumber = 2;
|
||||
|
||||
repeated Radial radials = 3;
|
||||
}
|
||||
|
||||
message Radial {
|
||||
fixed64 collectionTimestamp = 1;
|
||||
reserved 1,4,5;
|
||||
|
||||
int32 azimuthNumber = 2;
|
||||
float azimuthAngleDegrees = 3;
|
||||
float azimuthSpacingDegrees = 4;
|
||||
|
||||
RadialStatus radialStatus = 5;
|
||||
|
||||
int32 elevationNumber = 6;
|
||||
float elevationDegrees = 7;
|
||||
|
@ -24,16 +22,6 @@ message Radial {
|
|||
MomentaryProduct product = 8;
|
||||
}
|
||||
|
||||
enum RadialStatus {
|
||||
RADIAL_STATUS_UNKNOWN = 0;
|
||||
RADIAL_STATUS_ELEVATION_START = 1;
|
||||
RADIAL_STATUS_INTERMEDIATE_RADIAL_DATA = 2;
|
||||
RADIAL_STATUS_ELEVATION_END = 3;
|
||||
RADIAL_STATUS_VOLUME_SCAN_START = 4;
|
||||
RADIAL_STATUS_VOLUME_SCAN_END = 5;
|
||||
RADIAL_STATUS_ELEVATION_START_VCP_FINAL = 6;
|
||||
}
|
||||
|
||||
message MomentaryProduct {
|
||||
MomentaryMeta productMetadata = 1;
|
||||
MomentaryData data = 2;
|
||||
|
@ -44,11 +32,10 @@ message MomentaryMeta {
|
|||
}
|
||||
|
||||
message MomentaryData {
|
||||
float scale = 1;
|
||||
float offset = 2;
|
||||
reserved 1, 2, 5;
|
||||
|
||||
int32 startRange = 3;
|
||||
int32 sampleInterval = 4;
|
||||
|
||||
repeated int32 data = 5;
|
||||
repeated double data = 6;
|
||||
}
|
|
@ -119,8 +119,9 @@ pub fn parse(pal_str: &str) -> Result<Palette, PaletteParseError> {
|
|||
|
||||
let mut intermediate_with_infinities_added: Vec<(f64, u8, u8, u8, u8, u8, u8, u8, u8)> = vec![];
|
||||
|
||||
let first_negative = parsed_data[0].0.is_sign_negative();
|
||||
intermediate_with_infinities_added.push((
|
||||
f64::NEG_INFINITY,
|
||||
if first_negative { f64::NEG_INFINITY } else { f64::INFINITY },
|
||||
parsed_data[0].1,
|
||||
parsed_data[0].2,
|
||||
parsed_data[0].3,
|
||||
|
@ -132,7 +133,7 @@ pub fn parse(pal_str: &str) -> Result<Palette, PaletteParseError> {
|
|||
));
|
||||
intermediate_with_infinities_added.append(&mut parsed_data);
|
||||
intermediate_with_infinities_added.push((
|
||||
f64::INFINITY,
|
||||
if !first_negative { f64::NEG_INFINITY } else { f64::INFINITY },
|
||||
intermediate_with_infinities_added[intermediate_with_infinities_added.len() - 1].1,
|
||||
intermediate_with_infinities_added[intermediate_with_infinities_added.len() - 1].2,
|
||||
intermediate_with_infinities_added[intermediate_with_infinities_added.len() - 1].3,
|
||||
|
@ -143,6 +144,11 @@ pub fn parse(pal_str: &str) -> Result<Palette, PaletteParseError> {
|
|||
intermediate_with_infinities_added[intermediate_with_infinities_added.len() - 1].4,
|
||||
));
|
||||
|
||||
if !first_negative {
|
||||
// flip it
|
||||
intermediate_with_infinities_added.reverse();
|
||||
}
|
||||
|
||||
let mut output = vec![];
|
||||
|
||||
for range in intermediate_with_infinities_added.windows(2) {
|
||||
|
|
|
@ -7,7 +7,7 @@ mod tiles;
|
|||
|
||||
use crate::config::Config;
|
||||
use crate::grib2::{Grib2DataCache, Grib2TileCache, grib2_handler, grib2_metadata};
|
||||
use crate::nexrad::{NexradDataCache, NexradTileCache, nexrad_handler};
|
||||
use crate::nexrad::{NexradDataCache, NexradTileCache};
|
||||
use axum::Router;
|
||||
use axum::http::Method;
|
||||
use axum::routing::get;
|
||||
|
@ -68,7 +68,6 @@ async fn main() -> anyhow::Result<()> {
|
|||
let app = Router::new()
|
||||
.route("/grib2/{source}/{z}/{x}/{y}", get(grib2_handler))
|
||||
.route("/grib2/{source}/metadata", get(grib2_metadata))
|
||||
.route("/nexrad/{source}/{site}/{z}/{x}/{y}", get(nexrad_handler))
|
||||
.route(
|
||||
"/v2/nexrad/{source}/{site}/{sweep}/{product}",
|
||||
get(nexrad_data_handler),
|
||||
|
|
|
@ -18,16 +18,17 @@ use std::collections::HashMap;
|
|||
use std::f64::consts::PI;
|
||||
use std::fmt::Debug;
|
||||
use std::io;
|
||||
use std::io::ErrorKind;
|
||||
use std::io::{Cursor, ErrorKind};
|
||||
use std::sync::Arc;
|
||||
use tracing::{debug, info_span};
|
||||
use wxbox_ar2::sites::wsr88d::{SITES, Wsr88dSite};
|
||||
use wxbox_ar2::{MomentValue, Radial, Scan, Sweep, parse};
|
||||
use wxbox_ar2::parse::Ar2v;
|
||||
use wxbox_interchange::ar2::{Ar2AsCifParams};
|
||||
use wxbox_interchange::{serialize_cif_message, AsCif};
|
||||
use wxbox_pal::ColorPalette;
|
||||
|
||||
pub type NexradDataCache = Cache<DataId, Arc<wxbox_ar2::Scan>>;
|
||||
pub type NexradDataCache = Cache<DataId, Arc<Ar2v>>;
|
||||
pub type NexradTileCache = Cache<TileId, Arc<Vec<u8>>>;
|
||||
|
||||
pub type NexradDataConfig = HashMap<String, NexradDataSource>;
|
||||
|
@ -42,67 +43,6 @@ impl Debug for NexradDataSource {
|
|||
}
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "info")]
|
||||
pub async fn nexrad_handler(
|
||||
Path((source, site, z, x, y)): Path<(String, String, usize, usize, String)>,
|
||||
State(state): State<AppState>,
|
||||
) -> Result<impl IntoResponse, AppError> {
|
||||
debug!("here");
|
||||
let mut y = y
|
||||
.strip_suffix(".png")
|
||||
.ok_or(io::Error::new(ErrorKind::InvalidInput, "invalid"))?;
|
||||
let mut size = 256;
|
||||
if y.ends_with("@2x") {
|
||||
size = 512;
|
||||
y = y.strip_suffix("@2x").unwrap();
|
||||
}
|
||||
let y: usize = y.parse()?;
|
||||
let tile_id = TileId {
|
||||
source: format!("{source}/{site}"),
|
||||
z,
|
||||
x,
|
||||
y,
|
||||
size,
|
||||
};
|
||||
|
||||
// do we have a pre-prepared tile? if so, return it immediately
|
||||
if let Some(tile) = state.nexrad_tile_cache.get(&tile_id).await {
|
||||
return Ok(([(header::CONTENT_TYPE, "image/png")], tile.as_ref().clone()));
|
||||
}
|
||||
|
||||
// is this even a valid data source?
|
||||
let data_id = tile_id.data_id();
|
||||
let Some(ds) = state.config.data.nexrad.get(&source) else {
|
||||
return Err(anyhow!("invalid/unknown nexrad state").into());
|
||||
};
|
||||
|
||||
// ok, so we don't have a tile image yet
|
||||
// this means we are going to have to kick off a task to put that in the cache
|
||||
// lets check if we have the raw data
|
||||
let data = if !state.nexrad_data_cache.contains_key(&data_id) {
|
||||
// we don't, so let's start by starting a task for that
|
||||
load_nexrad_data(state.nexrad_data_cache, data_id, ds.clone(), &site).await?
|
||||
} else {
|
||||
state.nexrad_data_cache.get(&data_id).await.unwrap()
|
||||
};
|
||||
|
||||
// we know we need to build the tile, so let's do that now
|
||||
// it also returns it, so we can conveniently return it right now
|
||||
let pixel_data = render_to_png(
|
||||
state.nexrad_tile_cache.clone(),
|
||||
data,
|
||||
tile_id,
|
||||
&site,
|
||||
ds.clone(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok((
|
||||
[(header::CONTENT_TYPE, "image/png")],
|
||||
pixel_data.as_ref().clone(),
|
||||
))
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "info")]
|
||||
pub async fn nexrad_data_handler(
|
||||
Path((source, site, sweep, product)): Path<(String, String, u8, String)>,
|
||||
|
@ -146,7 +86,7 @@ async fn load_nexrad_data(
|
|||
data_id: DataId,
|
||||
_data_source: NexradDataSource,
|
||||
site: &str,
|
||||
) -> anyhow::Result<Arc<Scan>> {
|
||||
) -> anyhow::Result<Arc<Ar2v>> {
|
||||
let _load_span = info_span!("load_nexrad_data");
|
||||
|
||||
let mut searchdate = Utc::now();
|
||||
|
@ -201,8 +141,9 @@ async fn load_nexrad_data(
|
|||
}
|
||||
|
||||
let bytes = r.bytes().await?.to_vec();
|
||||
let mut r = Cursor::new(bytes);
|
||||
|
||||
let data = Arc::new(parse(bytes)?);
|
||||
let data = Arc::new(Ar2v::read(&mut r)?);
|
||||
|
||||
cache.insert(data_id, data.clone()).await;
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue