Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix typo vtps to vpts #164

Merged
merged 1 commit into from
Aug 5, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,10 +94,10 @@ The most important components of CROW are:

The source data are time series of vertical profiles (`vpts`)generated by [vol2bird](https://github.com/adokter/vol2bird). These are stored on a public file server. The text files contain fixed-width separated values of **variables related to the presence of birds** (density, speed, reflectivity, ...), grouped by date (first column), time (second column) and height (third column). The structure in the source data files is therefore a flat table. See for example the [data file for the radar at site Helchteren](https://opendata.meteo.be/ftp/observations/radar/vbird/behel/2020/behel_vpts_20201101.txt) on November 1st, 2020.

However for performance reasons, the `Home` compoment holds these data in a `radarVtps` variable organized as a tree of objects:
However for performance reasons, the `Home` compoment holds these data in a `radarVpts` variable organized as a tree of objects:

```
radarVtps (Object)
radarVpts (Object)
├── 1604185200000 (Object - timestamp)
│ ├── heightData (Object - vertical profile of birds for this timestamp, per altitude)
│ │ ├── 0 (Object)
Expand All @@ -113,10 +113,10 @@ radarVtps (Object)
└── ...
```

1. the `radarVtps` object is initialized according to the selected time range and radar. Sun altitudes at site are also computed and set.
2. data file(s) are loaded via AJAX, and their content is used to populate `radarVtps` (more specifically, the various properties in each timestamp -> heightdata entries).
1. the `radarVpts` object is initialized according to the selected time range and radar. Sun altitudes at site are also computed and set.
2. data file(s) are loaded via AJAX, and their content is used to populate `radarVpts` (more specifically, the various properties in each timestamp -> heightdata entries).
3. these data are transformed via computed properties and passed to the child components (that are in charge of the visulization):
- `VPChart` receives a flattened version of `radarVtps` (`radarVtpsAsArray`, similar to the structure of the initial data files)
- `VPChart` receives a flattened version of `radarVpts` (`radarVptsAsArray`, similar to the structure of the initial data files)
- `VPIChart` receives vertically integrated profiles (see `integrateProfile` function in `helpers.ts`).
- `TimelineChart` receives a simple array with the sun altitude for each shown time period.

Expand All @@ -128,7 +128,7 @@ It makes use of a few more child components for modularity reasons: `SiteSelecto

### VPChart

`VPChart` visualizes the raw VTPS data as a heatmap (bird density in function of the time and altitude) with D3. It uses the `DailyLines` component to show vertical lines on the chart each day at midnight and `ColorLegend` to show a legend for the 3 availables colour scales.
`VPChart` visualizes the raw VPTS data as a heatmap (bird density in function of the time and altitude) with D3. It uses the `DailyLines` component to show vertical lines on the chart each day at midnight and `ColorLegend` to show a legend for the 3 availables colour scales.

## License

Expand Down
10 changes: 5 additions & 5 deletions src/CrowTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,11 @@ export interface RadarInterface {
longitude: number;
timezone: string;
endpoint: string; // URL template, some variables are interpolated. Example: 'https://opendata.meteo.be/ftp/observations/radar/vbird/{odimCode}/{yyyy}/{odimCode}_vpts_{yyyymmdd}.txt'
vtpsFileFormat: VTPSFileFormat;
vptsFileFormat: VPTSFileFormat;
heights: number[]; // Data is available at the following heights
}

export type VTPSFileFormat = "VOL2BIRD" | "CSV"; // VOL2BIRD: fixed width column. CSV: Follow BioRad's output (see https://github.com/inbo/crow/issues/135)
export type VPTSFileFormat = "VOL2BIRD" | "CSV"; // VOL2BIRD: fixed width column. CSV: Follow BioRad's output (see https://github.com/inbo/crow/issues/135)

export interface GroupedRadarInterface {
label: string;
Expand All @@ -72,7 +72,7 @@ export interface TimeIntervalForRadioGroup {
value: number;
}

export interface VTPSDataRowFromFile {
export interface VPTSDataRowFromFile {
datetime: number;
height: number;
dd: number;
Expand All @@ -82,7 +82,7 @@ export interface VTPSDataRowFromFile {
eta: number;
}

export interface VTPSDataRow {
export interface VPTSDataRow {
datetime?: number;
height?: number;
dd?: number;
Expand All @@ -92,7 +92,7 @@ export interface VTPSDataRow {
noData?: boolean; // Field not in original data, but used to know if we have data loaded for the given datetime/height combination
}

export interface VTPSEntry { // TODO: check: is it a duplicate of VTPSDataRow?
export interface VPTSEntry { // TODO: check: is it a duplicate of VPTSDataRow?
dd: number;
dens: number;
ff: number;
Expand Down
62 changes: 31 additions & 31 deletions src/components/Home.vue
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@
<hr>

<v-p-chart
:vtps-data="radarVtpsAsArray"
:vpts-data="radarVptsAsArray"
:show-time-as="timeZoneToShow"
:style-config="VPChartStyle"
:scheme="VPChartSelectedScheme"
Expand Down Expand Up @@ -180,22 +180,22 @@ import SunCalc from "suncalc";
import config from "@/config";
import helpers from "@/helpers";

import { ColorSchemeIdentifier, IntegratedPropertyName, VTPSDataRowFromFile, TimeIntervalForRadioGroup, VTPSDataRow, VPIEntry, Period, TimeDisplayedAsValue, LangCode, MultilanguageStringContainer, Language, RadarInterface } from "@/CrowTypes";
import { ColorSchemeIdentifier, IntegratedPropertyName, VPTSDataRowFromFile, TimeIntervalForRadioGroup, VPTSDataRow, VPIEntry, Period, TimeDisplayedAsValue, LangCode, MultilanguageStringContainer, Language, RadarInterface } from "@/CrowTypes";
import { UserChoicesStoreModule } from "@/store/UserChoicesStore";
import { ConfigStoreModule } from "@/store/ConfigStore";
import { mapMutations } from "vuex";

interface VTPSDataByHeight {
[key: number]: VTPSDataRow;
interface VPTSDataByHeight {
[key: number]: VPTSDataRow;
}

interface RadarVTPSTreeEntry {
heightData: VTPSDataByHeight;
interface RadarVPTSTreeEntry {
heightData: VPTSDataByHeight;
sunAltitude: number;
}

interface RadarVtpsAsTree {
[key: number]: RadarVTPSTreeEntry;
interface RadarVptsAsTree {
[key: number]: RadarVPTSTreeEntry;
}

const initialCopyUrlText = "Copy link";
Expand Down Expand Up @@ -252,9 +252,9 @@ export default Vue.extend({

appTemporalResolution: config.appTemporalResolution as number,

// Data is kept as an object for performance reasons, the "radarVtpsAsArray" computed property allows reading it as an array.
// Data is kept as an object for performance reasons, the "radarVptsAsArray" computed property allows reading it as an array.
// All timestamps are kept in UTC (transformed later, in the viz components)
radarVtps: {} as RadarVtpsAsTree,
radarVpts: {} as RadarVptsAsTree,

publicPath: process.env.BASE_URL,
baseUrl: "",
Expand Down Expand Up @@ -395,10 +395,10 @@ export default Vue.extend({
return UserChoicesStoreModule.timeZoneToShow;
},
timePeriods(): Period[] {
// An array of all time periods currently shown (derived from radarVtps) with metadata such as the sun's position.
// An array of all time periods currently shown (derived from radarVpts) with metadata such as the sun's position.
const periods = [];

for (const [timestamp, metadataObj] of Object.entries(this.radarVtps)) {
for (const [timestamp, metadataObj] of Object.entries(this.radarVpts)) {
periods.push({
moment: moment.utc(+timestamp),
sunAltitude: metadataObj.sunAltitude
Expand All @@ -407,12 +407,12 @@ export default Vue.extend({

return periods;
},
radarVtpsAsArray(): VTPSDataRow[] {
radarVptsAsArray(): VPTSDataRow[] {
const dataArray = [];
for (const [timestamp, metadataObj] of Object.entries(this.radarVtps)) {
for (const [timestamp, metadataObj] of Object.entries(this.radarVpts)) {
for (const [height, props] of Object.entries(metadataObj.heightData)) {
const o = { timestamp: +timestamp, height: +height };
dataArray.push({ ...o, ...(props as VTPSDataRow) });
dataArray.push({ ...o, ...(props as VPTSDataRow) });
}
}
return dataArray;
Expand All @@ -435,13 +435,13 @@ export default Vue.extend({

integratedProfiles(): VPIEntry[] {
const integratedProfiles = [] as VPIEntry[];
for (const [timestamp, treeEntry] of Object.entries(this.radarVtps)) {
// VTPS values are stored in a tree per height, we need a flat array for integratedProfile
for (const [timestamp, treeEntry] of Object.entries(this.radarVpts)) {
// VPTS values are stored in a tree per height, we need a flat array for integratedProfile
const dataToIntegrate = [];
for (const [height, vtpsValues] of Object.entries(treeEntry.heightData)) {
for (const [height, vptsValues] of Object.entries(treeEntry.heightData)) {
const o = { height: +height };
dataToIntegrate.push({
...(vtpsValues as VTPSDataRowFromFile),
...(vptsValues as VPTSDataRowFromFile),
...o
});
}
Expand Down Expand Up @@ -531,17 +531,17 @@ export default Vue.extend({
onCopyUrl(): void {
this.copyUrlButtonText = "Link copied";
},
/* Initialize radarVtps with empty data
/* Initialize radarVpts with empty data
- The temporal range is [startMoment, endMoment] (resolution: appTemporalResolution - in seconds)
- Heights depend on the radar configuration
*/
initializeEmptyData(): void {
// Remove existing data
this.radarVtps = {} as RadarVtpsAsTree;
this.radarVpts = {} as RadarVptsAsTree;

const currentMoment = this.startMoment.clone();
while (currentMoment.isBefore(this.endMoment)) {
const heightObj = {} as VTPSDataByHeight;
const heightObj = {} as VPTSDataByHeight;
this.selectedRadar.heights.forEach(height => {
heightObj[height] = { noData: true };
});
Expand All @@ -558,7 +558,7 @@ export default Vue.extend({
};

this.$set(
this.radarVtps,
this.radarVpts,
currentMoment.toDate().getTime(),
metadataObj
);
Expand Down Expand Up @@ -595,19 +595,19 @@ export default Vue.extend({
});
},

/* Store a Vtps data row originating in a file into vtpsData */
storeDataRow(vtpsDataRow: VTPSDataRowFromFile): void {
const objToStore = { ...vtpsDataRow, ...{ noData: false } };
/* Store a Vpts data row originating in a file into vptsData */
storeDataRow(vptsDataRow: VPTSDataRowFromFile): void {
const objToStore = { ...vptsDataRow, ...{ noData: false } };

if ( // no (datetime) slot = data not copied. Allow automatic downsampling.
Object.prototype.hasOwnProperty.call(
this.radarVtps,
vtpsDataRow.datetime
this.radarVpts,
vptsDataRow.datetime
)
) {
this.$set(
this.radarVtps[vtpsDataRow.datetime].heightData,
vtpsDataRow.height,
this.radarVpts[vptsDataRow.datetime].heightData,
vptsDataRow.height,
objToStore
);
}
Expand Down Expand Up @@ -640,7 +640,7 @@ export default Vue.extend({
for (let currentDate of this.getDatesForData(startMoment, endMoment)) {
const url = helpers.buildVpTsDataUrl(radar, moment(currentDate, "YYYY-MM-DD"));
axios.get(url).then(response => {
const dayData = helpers.filterVtps(helpers.parseVtps(response.data, radar.vtpsFileFormat));
const dayData = helpers.filterVpts(helpers.parseVpts(response.data, radar.vptsFileFormat));

for (const val of dayData) {
this.storeDataRow(val);
Expand Down
26 changes: 13 additions & 13 deletions src/components/VPChart.vue
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
</g>
<g v-yaxis-left="{'scale': yScale}" />

<template v-for="d in vtpsDataPrepared">
<template v-for="d in vptsDataPrepared">
<rect
:id="'rect-' + d.timestamp + '-' + d.height"
:key="'rect-' + d.timestamp + '-' + d.height"
Expand Down Expand Up @@ -110,7 +110,7 @@ import ColorLegend from "@/components/ColorLegend.vue";
import moment, { Moment } from "moment-timezone";
import {
ColorSchemeIdentifier,
VTPSEntry,
VPTSEntry,
DayData,
ColorSchemeConfigEntry,
LangCode,
Expand All @@ -123,7 +123,7 @@ interface Scales {
y: null;
}

interface VTPSEntryPrepared extends VTPSEntry {
interface VPTSEntryPrepared extends VPTSEntry {
// Data, once ready for display
x: number;
y: number | undefined;
Expand Down Expand Up @@ -174,7 +174,7 @@ export default Vue.extend({
},
},
props: {
vtpsData: Array as () => VTPSEntry[],
vptsData: Array as () => VPTSEntry[],
styleConfig: Object,
scheme: String as () => ColorSchemeIdentifier,
showTimeAs: String, // "UTC" or a TZ database entry (such as "Europe/Brussels")
Expand Down Expand Up @@ -299,39 +299,39 @@ export default Vue.extend({
return durationInMs / 1000 / this.dataTemporalResolution + 1;
},
minTimestamp: function (): number {
const minVal = d3.min(this.vtpsData, function (d: VTPSEntry) {
const minVal = d3.min(this.vptsData, function (d: VPTSEntry) {
return d.timestamp;
});
return minVal || 0;
},
maxTimestamp: function (): number {
const maxVal = d3.max(this.vtpsData, function (d: VTPSEntry) {
const maxVal = d3.max(this.vptsData, function (d: VPTSEntry) {
return d.timestamp;
});
return maxVal || 0;
},
maxColorLegendValue: function(): number {
// We need a prop for this so it gets updated when data is added to vtpsData (and maxDensity is subsequently changed)
// We need a prop for this so it gets updated when data is added to vptsData (and maxDensity is subsequently changed)
if (this.selectedColorSchemeConfig.dynamicDomain === true) {
return this.maxDensity
} else {
return this.selectedColorSchemeConfig.colorScale.domain().reduce(function (a, b) {return Math.max(a, b)}) // Get the highest value so we support inverted domains (to reverse color scales)
}
},
maxDensity: function (): number {
const maxVal = d3.max(this.vtpsData, function (d) {
const maxVal = d3.max(this.vptsData, function (d) {
return d.dens;
});
return maxVal || 0;
},
dataTemporalResolution: function (): number {
return (this.vtpsData[this.distinctHeightsMeters.length + 1].timestamp - this.vtpsData[0].timestamp) / 1000;
return (this.vptsData[this.distinctHeightsMeters.length + 1].timestamp - this.vptsData[0].timestamp) / 1000;
},
dataVerticalResolutionMeters: function(): number {
return this.distinctHeightsMeters[1] - this.distinctHeightsMeters[0];
},
distinctHeightsMeters: function (): number[] {
const heightsSet = new Set(this.vtpsData.map((row) => row.height));
const heightsSet = new Set(this.vptsData.map((row) => row.height));
return Array.from(heightsSet.values());
},
distinctHeightsMetersPlusOne: function (): number[] {
Expand Down Expand Up @@ -369,8 +369,8 @@ export default Vue.extend({
.range([this.innerHeight, 0])
.domain([0, this.maxHeightFeet]);
},
vtpsDataPrepared: function (): VTPSEntryPrepared[] {
return this.vtpsData.map((data) => ({
vptsDataPrepared: function (): VPTSEntryPrepared[] {
return this.vptsData.map((data) => ({
...data,

x: Math.round(Math.round(this.xScale(data.timestamp)) + 1),
Expand Down Expand Up @@ -428,7 +428,7 @@ export default Vue.extend({
return 0;
}
},
getRectColor: function (data: VTPSEntry): string {
getRectColor: function (data: VPTSEntry): string {
let color;
const config = this.selectedColorSchemeConfig;

Expand Down
Loading