geojson#Point TypeScript Examples
The following examples show how to use
geojson#Point.
You can vote up the ones you like or vote down the ones you don't like,
and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: ews-utils.ts From prism-frontend with MIT License | 6 votes |
fetchEWSData = async (
baseUrl: string,
date: number,
): Promise<PointLayerData> => {
const [locations, values] = await Promise.all([
fetchEWSLocations(baseUrl),
fetchEWSDataPointsByLocation(baseUrl, date),
]);
const processedFeatures: PointData[] = locations.features.reduce(
(pointDataArray, feature) => {
const { properties, geometry } = feature;
if (!properties) {
return pointDataArray;
}
const locationValues: number[] = values
.filter(v => v.location_id === properties.id)
.map(v => v.value[1]);
if (locationValues.length === 0) {
return pointDataArray;
}
const mean =
locationValues.reduce((acc, item) => acc + item, 0) /
locationValues.length;
const min = Math.min(...locationValues);
const max = Math.max(...locationValues);
const { coordinates } = geometry as Point;
const pointData: PointData = {
lon: coordinates[0],
lat: coordinates[1],
date,
mean: parseFloat(mean.toFixed(2)),
min,
max,
...properties,
status: getLevelStatus(
max,
properties.trigger_levels as EWSTriggerLevels,
),
};
return [...pointDataArray, pointData];
},
[] as PointData[],
);
return {
features: GeoJSON.parse(processedFeatures, {
Point: ['lat', 'lon'],
}),
};
}
Example #2
Source File: test.service.ts From aqualink-app with MIT License | 6 votes |
private async loadMocks(connection: Connection) {
await connection.getRepository(User).save(users);
await connection.getRepository(Site).save(sites);
await connection.getRepository(SiteSurveyPoint).save(surveyPoints);
await connection.getRepository(SiteApplication).save(siteApplications);
await connection.getRepository(Sources).save(sources);
await connection.getRepository(TimeSeries).save(timeSeries);
await connection.query('REFRESH MATERIALIZED VIEW latest_data');
await connection.getRepository(Collection).save(collections);
await connection.getRepository(DailyData).save(dailyData);
await connection.getRepository(Survey).save(surveys);
await connection.getRepository(SurveyMedia).save(surveyMedia);
await Bluebird.map(sites, async (site) => {
const [longitude, latitude] = (site.polygon as Point).coordinates;
const historicalMonthlyMean = await getHistoricalMonthlyMeans(
longitude,
latitude,
);
return Bluebird.map(historicalMonthlyMean, (hmm) => {
return connection.getRepository(HistoricalMonthlyMean).save({
site,
month: hmm.month,
temperature: hmm.temperature,
});
});
});
}
Example #3
Source File: sofar-availability.ts From aqualink-app with MIT License | 6 votes |
export function getSofarWaveModelAvailability(): FeatureCollection<Point> {
if (!geojson) {
geojson = {
type: 'FeatureCollection',
features: availabilityPoints.map((coordinate) => ({
type: 'Feature',
geometry: {
type: 'Point',
coordinates: coordinate,
},
})),
};
}
return geojson;
}
Example #4
Source File: coordinates.ts From aqualink-app with MIT License | 6 votes |
createPoint = (
longitude: number,
latitude: number,
numberOfDecimals: number = 5,
): Point => {
const precision = 10 ** numberOfDecimals;
return {
type: 'Point',
coordinates: [
Math.round((longitude + Number.EPSILON) * precision) / precision,
Math.round((latitude + Number.EPSILON) * precision) / precision,
],
};
}
Example #5
Source File: augment-site-data.ts From aqualink-app with MIT License | 6 votes |
async function getAugmentedData(
site: Site,
regionRepository: Repository<Region>,
) {
const [longitude, latitude] = (site.polygon as Point).coordinates;
const region =
site.region || (await getRegion(longitude, latitude, regionRepository));
const MMM = await getMMM(longitude, latitude);
if (MMM === null) {
console.warn(
`Max Monthly Mean appears to be null for Site ${site.name} at (lat, lon): (${latitude}, ${longitude}) `,
);
}
const timezones = geoTz(latitude, longitude);
return omitBy(
{
region,
timezone: timezones.length > 0 ? timezones[0] : null,
maxMonthlyMean: MMM,
},
isNil,
);
}
Example #6
Source File: RegionLabelLayer.tsx From Teyvat.moe with GNU General Public License v3.0 | 6 votes |
_RegionLabelLayer: FunctionComponent<RegionLabelLayerProps> = ({ displayed, zoomLevel }) => {
const classes = useStyles();
const map = useMap();
const layerReference = useRef<GeoJSONLeaflet | null>(null);
useEffect(() => {
if (layerReference.current != null) {
if (displayed) {
layerReference.current.addTo(map);
} else {
layerReference.current.removeFrom(map);
}
}
}, [map, displayed]);
const pointToLayer = (featureData: Feature<Point, any>, latLng: LatLng) => {
const html = renderToString(<RegionLabel featureData={featureData} zoomLevel={zoomLevel} />);
return LeafletMarker([latLng.lng, latLng.lat], {
interactive: false, // Allow clicks to pass through.
icon: LeafletDivIcon({
html,
className: classes.regionLabelMarker,
}),
zIndexOffset: -900,
});
};
return (
<GeoJSON
ref={layerReference}
key={zoomLevel}
pointToLayer={pointToLayer}
data={RegionLabelData as GeoJsonObject}
/>
);
}
Example #7
Source File: sensors.service.ts From aqualink-app with MIT License | 5 votes |
async findSensors(): Promise<
(Site & { sensorPosition: GeoJSON; sensorType: SensorType })[]
> {
const sites = await this.siteRepository.find({
where: { sensorId: Not(IsNull()) },
});
// Get spotter data and add site id to distinguish them
const spotterData = await Bluebird.map(
sites,
(site) => {
if (site.sensorId === null) {
console.warn(`Spotter for site ${site.id} appears null.`);
}
return getSpotterData(site.sensorId!).then((data) => {
return {
id: site.id,
...data,
};
});
},
{ concurrency: 10 },
);
// Group spotter data by site id for easier search
const siteIdToSpotterData: Record<number, SpotterData & { id: number }> =
keyBy(spotterData, (o) => o.id);
// Construct final response
return sites.map((site) => {
const data = siteIdToSpotterData[site.id];
const longitude = getLatestData(data.longitude)?.value;
const latitude = getLatestData(data.latitude)?.value;
const sitePosition = site.polygon as Point;
// If no longitude or latitude is provided by the spotter fallback to the site coordinates
return {
...site,
applied: site.applied,
sensorPosition: createPoint(
longitude || sitePosition.coordinates[0],
latitude || sitePosition.coordinates[1],
),
sensorType: SensorType.SofarSpotter,
};
});
}
Example #8
Source File: augment-site-data.ts From aqualink-app with MIT License | 5 votes |
async function augmentSites(connection: Connection) {
const siteRepository = connection.getRepository(Site);
const regionRepository = connection.getRepository(Region);
const HistoricalMonthlyMeanRepository = connection.getRepository(
HistoricalMonthlyMean,
);
const allSites = await siteRepository.find();
const start = new Date();
console.log(`Augmenting ${allSites.length} sites...`);
await Bluebird.map(
allSites,
async (site) => {
const augmentedData = await getAugmentedData(site, regionRepository);
await siteRepository.update(site.id, augmentedData);
// Add HistoricalMonthlyMeans
const [longitude, latitude] = (site.polygon as Point).coordinates;
const HistoricalMonthlyMeans = await getHistoricalMonthlyMeans(
longitude,
latitude,
);
await Promise.all(
HistoricalMonthlyMeans.map(async ({ month, temperature }) => {
try {
await (temperature &&
HistoricalMonthlyMeanRepository.insert({
site,
month,
temperature,
}));
} catch {
console.warn(
`Monthly max values not imported for ${site.id} - the data was likely there already.`,
);
}
}),
);
},
{ concurrency: 1 },
);
console.log(
`Augmented ${allSites.length} sites in ${
(new Date().valueOf() - start.valueOf()) / 1000
} seconds`,
);
}
Example #9
Source File: liveData.ts From aqualink-app with MIT License | 5 votes |
getLiveData = async (
site: Site,
isDeployed: boolean,
): Promise<SofarLiveData> => {
console.time(`getLiveData for site ${site.id}`);
const { polygon, sensorId, maxMonthlyMean } = site;
// TODO - Accept Polygon option
const [longitude, latitude] = (polygon as Point).coordinates;
const now = new Date();
const [spotterRawData, degreeHeatingDays, satelliteTemperature] =
await Promise.all([
sensorId && isDeployed ? getSpotterData(sensorId) : undefined,
getDegreeHeatingDays(latitude, longitude, now, maxMonthlyMean),
getSofarHindcastData(
SofarModels.NOAACoralReefWatch,
sofarVariableIDs[SofarModels.NOAACoralReefWatch]
.analysedSeaSurfaceTemperature,
latitude,
longitude,
now,
96,
),
]);
const spotterData = spotterRawData
? {
topTemperature: getLatestData(spotterRawData.topTemperature),
bottomTemperature: getLatestData(spotterRawData.bottomTemperature),
longitude:
spotterRawData.longitude && getLatestData(spotterRawData.longitude),
latitude:
spotterRawData.latitude && getLatestData(spotterRawData.latitude),
}
: {};
const filteredValues = omitBy(
{
degreeHeatingDays,
satelliteTemperature:
satelliteTemperature && getLatestData(satelliteTemperature),
// Override all possible values with spotter data.
...spotterData,
},
(data) => isNil(data?.value) || data?.value === 9999,
);
const dailyAlertLevel = calculateAlertLevel(
maxMonthlyMean,
filteredValues?.satelliteTemperature?.value,
degreeHeatingDays?.value,
);
console.timeEnd(`getLiveData for site ${site.id}`);
return {
site: { id: site.id },
...filteredValues,
...(spotterData.longitude &&
spotterData.latitude && {
spotterPosition: {
longitude: spotterData.longitude,
latitude: spotterData.latitude,
},
}),
dailyAlertLevel,
};
}
Example #10
Source File: upload-hobo-data.ts From aqualink-app with MIT License | 5 votes |
getSiteRecords = async (
dataAsJson: Coords[],
siteIds: number[],
regionRepository: Repository<Region>,
) => {
// Group by site
const recordsGroupedBySite = groupBy(dataAsJson, 'site');
// Extract site entities and calculate position of site by averaging all each surveyPoints positions
const sites = await Promise.all(
siteIds.map((siteId) => {
// Filter out NaN values
const filteredSiteCoords = recordsGroupedBySite[siteId].filter(
(record) => !isNaN(record.lat) && !isNaN(record.long),
);
const siteRecord = filteredSiteCoords.reduce(
(previous, record) => {
return {
...previous,
lat: previous.lat + record.lat,
long: previous.long + record.long,
};
},
{ site: siteId, colony: 0, lat: 0, long: 0 },
);
// Calculate site position
const point: Point = createPoint(
siteRecord.long / filteredSiteCoords.length,
siteRecord.lat / filteredSiteCoords.length,
);
// Augment site information
const [longitude, latitude] = point.coordinates;
const timezones = getTimezones(latitude, longitude) as string[];
return Promise.all([
getRegion(longitude, latitude, regionRepository),
getMMM(longitude, latitude),
]).then(([region, maxMonthlyMean]) => ({
name: SITE_PREFIX + siteId,
polygon: point,
region,
maxMonthlyMean,
display: false,
timezone: timezones[0],
status: SiteStatus.Approved,
}));
}),
);
return { recordsGroupedBySite, sites };
}
Example #11
Source File: upload-hobo-data.ts From aqualink-app with MIT License | 5 votes |
createSites = async (
sites: Partial<Site>[],
user: User,
siteRepository: Repository<Site>,
userRepository: Repository<User>,
historicalMonthlyMeanRepository: Repository<HistoricalMonthlyMean>,
) => {
logger.log('Saving sites');
const siteEntities = await Promise.all(
sites.map((site) =>
siteRepository
.save(site)
.catch(handleEntityDuplicate(siteRepository, siteQuery, site.polygon)),
),
);
logger.log(`Saving monthly max data`);
await Bluebird.map(
siteEntities,
(site) => {
const point: Point = site.polygon as Point;
const [longitude, latitude] = point.coordinates;
return Promise.all([
getHistoricalMonthlyMeans(longitude, latitude),
historicalMonthlyMeanRepository.findOne({ where: { site } }),
]).then(([historicalMonthlyMean, found]) => {
if (found || !historicalMonthlyMean) {
logger.warn(`Site ${site.id} has already monthly max data`);
return null;
}
return historicalMonthlyMean.map(({ month, temperature }) => {
return (
temperature &&
historicalMonthlyMeanRepository.save({ site, month, temperature })
);
});
});
},
{ concurrency: 4 },
);
// Create reverse map (db.site.id => csv.site_id)
const dbIdToCSVId: Record<number, number> = Object.fromEntries(
siteEntities.map((site) => {
if (!site.name) {
throw new InternalServerErrorException('Site name was not defined');
}
const siteId = parseInt(site.name.replace(SITE_PREFIX, ''), 10);
return [site.id, siteId];
}),
);
// Update administered sites relationship
await userRepository.save({
id: user.id,
administeredSites: user.administeredSites.concat(siteEntities),
});
return { siteEntities, dbIdToCSVId };
}
Example #12
Source File: upload-hobo-data.ts From aqualink-app with MIT License | 5 votes |
createSurveyPoints = async (
siteEntities: Site[],
dbIdToCSVId: Record<number, number>,
recordsGroupedBySite: Dictionary<Coords[]>,
rootPath: string,
poiRepository: Repository<SiteSurveyPoint>,
) => {
// Create site points of interest entities for each imported site
// Final result needs to be flattened since the resulting array is grouped by site
const surveyPoints = siteEntities
.map((site) => {
const currentSiteId = dbIdToCSVId[site.id];
const siteFolder = FOLDER_PREFIX + currentSiteId;
return recordsGroupedBySite[currentSiteId]
.filter((record) => {
const colonyId = record.colony.toString().padStart(3, '0');
const colonyFolder = COLONY_FOLDER_PREFIX + colonyId;
const colonyFolderPath = path.join(
rootPath,
siteFolder,
colonyFolder,
);
return fs.existsSync(colonyFolderPath);
})
.map((record) => {
const point: Point | undefined =
!isNaN(record.long) && !isNaN(record.lat)
? createPoint(record.long, record.lat)
: undefined;
return {
name: COLONY_PREFIX + record.colony,
site,
polygon: point,
};
});
})
.flat();
logger.log('Saving site points of interest');
const poiEntities = await Promise.all(
surveyPoints.map((poi) =>
poiRepository
.save(poi)
.catch(handleEntityDuplicate(poiRepository, poiQuery, poi.polygon)),
),
);
return poiEntities;
}
Example #13
Source File: geospatialService.ts From react-flight-tracker with MIT License | 5 votes |
private calculatePath = (stateVectors: IStateVectorData) => {
const features: Array<Feature<Point, GeoJsonProperties>> = [];
for (var stateVector of stateVectors.states) {
// Setup last position time in ms
var lastPositionTime = this.pathPredictionCounter
// Setup altitude in m
var altitude = stateVector.geo_altitude;
if ((altitude === null) || (altitude < 0))
altitude = stateVector.baro_altitude;
if ((altitude === null) || (altitude < 0))
altitude = 0;
// Setup vertical rate
var verticalRate = stateVector.vertical_rate ? stateVector.vertical_rate : 0.0;
if (verticalRate < 0)
verticalRate *= -1;
const origin: Array<number> = [stateVector.longitude ? stateVector.longitude : 0, stateVector.latitude ? stateVector.latitude : 0]
const velocity = stateVector.velocity ? stateVector.velocity : 0;
var distance = (velocity * lastPositionTime) / 1000;
// Try to adjust the distance to the vertical rate
if (verticalRate !== 0)
distance = distance - (verticalRate * (lastPositionTime / 1000));
// Try to adjust the distance to the altitude
if (altitude > 0)
distance = (distance * earthRadius) / (earthRadius + altitude);
const bearing = stateVector.true_track ? stateVector.true_track : 0;
// Calculate the destination
const feature = destination(
origin,
distance,
bearing,
{
units: "meters"
}
);
// Adding the ICAO24 prop to the feature so that a corresponding assignment is possible later
var properties: GeoJsonProperties = {
['icao24']: stateVector.icao24
};
feature.properties = properties;
// Push the feature to the collection
features.push(feature);
}
// Increase counter time
this.pathPredictionCounter += this.pathPredictionInterval;
// Execute callbacks
Object.entries(this.pathPredictionUpdatedSubscriberDictionary).forEach(([key, value], index) => value(features))
};
Example #14
Source File: sst-backfill.ts From aqualink-app with MIT License | 4 votes |
async function main() {
const connection = await createConnection(dbConfig);
const siteRepository = connection.getRepository(Site);
const dailyDataRepository = connection.getRepository(DailyData);
const sourcesRepository = connection.getRepository(Sources);
const timeSeriesRepository = connection.getRepository(TimeSeries);
const selectedSites = await siteRepository.find({
where:
sitesToProcess.length > 0
? {
id: In(sitesToProcess),
}
: {},
});
const dailyDataEntities = selectedSites.reduce(
(entities: DailyData[], site) => {
console.log(`Processing site ${site.name || site.id}...`);
const allYearEntities = yearsArray.reduce(
(yearEntities: DailyData[], year) => {
console.log(`Processing year ${year}...`);
const [longitude, latitude] = (site.polygon as Point).coordinates;
const data = getNOAAData(year, longitude, latitude);
const yearEntitiesForSite = data.map(
({ date, satelliteTemperature }) =>
({
site: { id: site.id },
date,
satelliteTemperature,
} as DailyData),
);
return yearEntities.concat(yearEntitiesForSite);
},
[],
);
return entities.concat(allYearEntities);
},
[],
);
const sources = await Promise.all(
selectedSites.map((site) => {
return getNOAASource(site, sourcesRepository);
}),
);
const siteToSource: Record<number, Sources> = keyBy(
sources,
(source) => source.site.id,
);
await Bluebird.map(dailyDataEntities, async (entity) => {
try {
await dailyDataRepository.save(entity);
} catch (err) {
if (err.constraint === 'no_duplicated_date') {
console.debug(
`Data already exists for this date ${entity.date.toDateString()}`,
);
} else {
console.error(err);
}
}
if (!entity.satelliteTemperature) {
return;
}
await insertSiteDataToTimeSeries(
[
{
value: entity.satelliteTemperature,
timestamp: entity.date.toISOString(),
},
],
Metric.SATELLITE_TEMPERATURE,
siteToSource[entity.site.id],
timeSeriesRepository,
);
});
// Update materialized view
console.log('Refreshing materialized view latest_data');
await connection.query('REFRESH MATERIALIZED VIEW latest_data');
connection.close();
process.exit(0);
}
Example #15
Source File: hindcast-wind-wave.ts From aqualink-app with MIT License | 4 votes |
addWindWaveData = async (
siteIds: number[],
repositories: Repositories,
) => {
logger.log('Fetching sites');
// Fetch all sites
const sites = await getSites(siteIds, false, repositories.siteRepository);
const date = new Date();
const yesterdayDate = new Date(date);
yesterdayDate.setDate(date.getDate() - 1);
const today = date.toISOString();
const yesterday = yesterdayDate.toISOString();
logger.log('Saving wind & wave forecast data');
await Bluebird.map(
sites,
async (site) => {
const { polygon } = site;
const [longitude, latitude] = getSofarNearestAvailablePoint(
polygon as Point,
);
logger.log(
`Saving wind & wave forecast data for ${site.id} at ${latitude} - ${longitude}`,
);
const hindcastOptions = [
[
SofarModels.SofarOperationalWaveModel,
sofarVariableIDs[SofarModels.SofarOperationalWaveModel]
.significantWaveHeight,
],
[
SofarModels.SofarOperationalWaveModel,
sofarVariableIDs[SofarModels.SofarOperationalWaveModel].meanDirection,
],
[
SofarModels.SofarOperationalWaveModel,
sofarVariableIDs[SofarModels.SofarOperationalWaveModel].meanPeriod,
],
[
SofarModels.GFS,
sofarVariableIDs[SofarModels.GFS].windVelocity10MeterEastward,
],
[
SofarModels.GFS,
sofarVariableIDs[SofarModels.GFS].windVelocity10MeterNorthward,
],
];
const response = await Promise.all(
hindcastOptions.map(([sofarModel, sofarVariableId]) => {
return sofarHindcast(
sofarModel,
sofarVariableId,
latitude,
longitude,
yesterday,
today,
);
}),
);
const [
significantWaveHeight,
waveMeanDirection,
waveMeanPeriod,
windVelocity10MeterEastward,
windVelocity10MeterNorthward,
] = response.map((x) => {
if (!x || x.values.length < 1) return undefined;
return x.values[x.values.length - 1]; // latest available forecast in the past
});
// Calculate wind speed and direction from velocity
// TODO: treat undefined better
const windNorthwardVelocity = windVelocity10MeterNorthward?.value;
const windEastwardVelocity = windVelocity10MeterEastward?.value;
const sameTimestamps =
windVelocity10MeterEastward?.timestamp ===
windVelocity10MeterNorthward?.timestamp;
const windSpeed: ValueWithTimestamp | undefined =
windNorthwardVelocity && windEastwardVelocity && sameTimestamps
? {
timestamp: windVelocity10MeterNorthward?.timestamp,
value: getWindSpeed(windEastwardVelocity, windNorthwardVelocity),
}
: undefined;
const windDirection: ValueWithTimestamp | undefined =
windNorthwardVelocity && windEastwardVelocity && sameTimestamps
? {
timestamp: windVelocity10MeterNorthward?.timestamp,
value: getWindDirection(
windEastwardVelocity,
windNorthwardVelocity,
),
}
: undefined;
const forecastData = {
significantWaveHeight,
waveMeanDirection,
waveMeanPeriod,
windSpeed,
windDirection,
};
// Save wind wave data to forecast_data
await Promise.all(
// eslint-disable-next-line array-callback-return, consistent-return
dataLabels.map(([dataLabel, metric]) => {
const sofarValue = forecastData[dataLabel] as ValueWithTimestamp;
if (!isNil(sofarValue?.value) && !Number.isNaN(sofarValue?.value)) {
return repositories.hindcastRepository
.createQueryBuilder('forecast_data')
.insert()
.values([
{
site,
timestamp: moment(sofarValue.timestamp)
.startOf('minute')
.toDate(),
metric,
value: sofarValue.value,
updatedAt: today,
},
])
.onConflict(
`ON CONSTRAINT "one_row_per_site_per_metric" DO UPDATE SET "timestamp" = excluded."timestamp", "updated_at" = excluded."updated_at", "value" = excluded."value"`,
)
.execute();
}
}),
);
},
{ concurrency: 4 },
);
logger.log('Completed updating hindcast data');
}
Example #16
Source File: sst-time-series.ts From aqualink-app with MIT License | 4 votes |
updateSST = async (
siteIds: number[],
days: number,
connection: Connection,
repositories: Repositories,
) => {
const { siteRepository, timeSeriesRepository, sourceRepository } =
repositories;
logger.log('Fetching sites');
// Fetch sites entities
const sites = await getSites(siteIds, siteRepository);
// Fetch sources
const sources = await Promise.all(
sites.map((site) => {
return getNOAASource(site, sourceRepository);
}),
);
await Bluebird.map(
sources,
async (source) => {
const { site } = source;
const point = site.polygon as Point;
// Extract site coordinates
const [longitude, latitude] = point.coordinates;
logger.log(`Back-filling site with id ${site.id}.`);
const data = await Bluebird.map(
times(days),
// A non-async function is used on purpose.
// We need for as many http request to be performed simultaneously without one blocking the other
// This way we get a much greater speed up due to the concurrency.
(i) => {
const endDate =
i === 0
? moment().format()
: moment().subtract(i, 'd').endOf('day').format();
const startDate = moment().subtract(i, 'd').startOf('day').format();
// use Promise/then to increase concurrency since await halts the event loop
return Promise.all([
// Fetch satellite surface temperature data
sofarHindcast(
SofarModels.NOAACoralReefWatch,
sofarVariableIDs[SofarModels.NOAACoralReefWatch]
.analysedSeaSurfaceTemperature,
latitude,
longitude,
startDate,
endDate,
),
// Fetch degree heating weeks data
sofarHindcast(
SofarModels.NOAACoralReefWatch,
sofarVariableIDs[SofarModels.NOAACoralReefWatch]
.degreeHeatingWeek,
latitude,
longitude,
startDate,
endDate,
),
]).then(([SofarSSTRaw, sofarDegreeHeatingWeekRaw]) => {
// Filter out null values
const sstFiltered = filterSofarResponse(SofarSSTRaw);
const dhwFiltered = filterSofarResponse(sofarDegreeHeatingWeekRaw);
// Get latest dhw
const latestDhw = getLatestData(dhwFiltered);
// Get alert level
const alertLevel = calculateAlertLevel(
site.maxMonthlyMean,
getLatestData(sstFiltered)?.value,
// Calculate degree heating days
latestDhw && latestDhw.value * 7,
);
// Calculate the sstAnomaly
const sstAnomaly = sstFiltered
.map((sst) => ({
value: getSstAnomaly(site.historicalMonthlyMean, sst),
timestamp: sst.timestamp,
}))
// Filter out null values
.filter((sstAnomalyValue) => {
return !isNil(sstAnomalyValue.value);
}) as ValueWithTimestamp[];
// return calculated metrics (sst, dhw, sstAnomaly alert)
return {
sst: sstFiltered,
dhw: dhwFiltered,
sstAnomaly,
alert:
alertLevel !== undefined
? [
{
value: alertLevel,
timestamp: moment().subtract(i, 'd').hour(12).format(),
},
]
: [],
};
});
},
{ concurrency: 100 },
);
return Bluebird.map(
data,
// Save data on time_series table
({ sst, dhw, alert, sstAnomaly }) =>
Promise.all([
insertSiteDataToTimeSeries(
sst,
Metric.SATELLITE_TEMPERATURE,
source,
timeSeriesRepository,
),
insertSiteDataToTimeSeries(
dhw,
Metric.DHW,
source,
timeSeriesRepository,
),
insertSiteDataToTimeSeries(
alert,
Metric.ALERT,
source,
timeSeriesRepository,
),
insertSiteDataToTimeSeries(
sstAnomaly,
Metric.SST_ANOMALY,
source,
timeSeriesRepository,
),
]),
{ concurrency: 100 },
);
},
// Speed up if this is just a daily update
// Concurrency should remain low, otherwise it will overwhelm the sofar api server
{ concurrency: days <= 5 ? 10 : 1 },
);
logger.log('Back-filling weekly alert level');
// We calculate weekly alert separately because it depends on values of alert levels across 7 days
await Bluebird.map(
times(days),
async (i) => {
const endDate =
i === 0
? moment().format()
: moment().subtract(i, 'd').endOf('day').format();
logger.log(`Back-filling weekly alert for ${endDate}`);
// Calculate max alert by fetching the max alert in the last 7 days
// As timestamp it is selected the latest available timestamp
const maxAlert = await repositories.timeSeriesRepository
.createQueryBuilder('time_series')
.select('MAX(value)', 'value')
.addSelect('source_id', 'source')
.addSelect('MAX(timestamp)', 'timestamp')
.where('timestamp <= :endDate::timestamp', { endDate })
.andWhere("timestamp >= :endDate::timestamp - INTERVAL '7 days'", {
endDate,
})
.andWhere('metric = :alertMetric', { alertMetric: Metric.ALERT })
.andWhere('source_id IN (:...sourceIds)', {
sourceIds: sources.map((source) => source.id),
})
.groupBy('source_id')
.getRawMany();
await repositories.timeSeriesRepository
.createQueryBuilder('time_series')
.insert()
.values(
maxAlert.map((data) => ({
...data,
metric: Metric.WEEKLY_ALERT,
})),
)
.onConflict('ON CONSTRAINT "no_duplicate_data" DO NOTHING')
.execute();
},
// Concurrency is set to 1 to avoid read and writing the table time_series at the same time
{ concurrency: 1 },
);
// Update materialized view
logger.log('Refreshing materialized view latest_data');
connection.query('REFRESH MATERIALIZED VIEW latest_data');
}
Example #17
Source File: dailyData.ts From aqualink-app with MIT License | 4 votes |
export async function getDailyData(
site: Site,
endOfDate: Date,
excludedDates: ExclusionDates[],
): Promise<SofarDailyData> {
const { polygon, sensorId, maxMonthlyMean } = site;
// TODO - Accept Polygon option
const [longitude, latitude] = (polygon as Point).coordinates;
const [
spotterRawData,
degreeHeatingDays,
satelliteTemperatureData,
significantWaveHeightsRaw,
waveMeanDirectionRaw,
waveMeanPeriodRaw,
windVelocity10MeterEastward,
windVelocity10MeterNorthward,
] = await Promise.all([
sensorId ? getSpotterData(sensorId, endOfDate) : DEFAULT_SPOTTER_DATA_VALUE,
// Calculate Degree Heating Days
// Calculating Degree Heating Days requires exactly 84 days of data.
getDegreeHeatingDays(latitude, longitude, endOfDate, maxMonthlyMean),
getSofarHindcastData(
SofarModels.NOAACoralReefWatch,
sofarVariableIDs[SofarModels.NOAACoralReefWatch]
.analysedSeaSurfaceTemperature,
latitude,
longitude,
endOfDate,
96,
),
getSofarHindcastData(
SofarModels.SofarOperationalWaveModel,
sofarVariableIDs[SofarModels.SofarOperationalWaveModel]
.significantWaveHeight,
latitude,
longitude,
endOfDate,
).then((data) => data.map(({ value }) => value)),
getSofarHindcastData(
SofarModels.SofarOperationalWaveModel,
sofarVariableIDs[SofarModels.SofarOperationalWaveModel].meanDirection,
latitude,
longitude,
endOfDate,
).then((data) => data.map(({ value }) => value)),
getSofarHindcastData(
SofarModels.SofarOperationalWaveModel,
sofarVariableIDs[SofarModels.SofarOperationalWaveModel].meanPeriod,
latitude,
longitude,
endOfDate,
).then((data) => data.map(({ value }) => value)),
// Get NOAA GFS wind data
getSofarHindcastData(
SofarModels.GFS,
sofarVariableIDs[SofarModels.GFS].windVelocity10MeterEastward,
latitude,
longitude,
endOfDate,
).then((data) => data.map(({ value }) => value)),
getSofarHindcastData(
SofarModels.GFS,
sofarVariableIDs[SofarModels.GFS].windVelocity10MeterNorthward,
latitude,
longitude,
endOfDate,
).then((data) => data.map(({ value }) => value)),
]);
const spotterData = spotterRawData
? mapValues(spotterRawData, (v) =>
extractSofarValues(filterMetricDataByDate(excludedDates, v)),
)
: {
topTemperature: [],
bottomTemperature: [],
significantWaveHeight: [],
waveMeanPeriod: [],
waveMeanDirection: [],
windSpeed: [],
windDirection: [],
};
const minBottomTemperature = getMin(spotterData.bottomTemperature);
const maxBottomTemperature = getMax(spotterData.bottomTemperature);
const avgBottomTemperature = getAverage(spotterData.bottomTemperature);
const topTemperature = getAverage(spotterData.topTemperature);
// Get satelliteTemperature
const latestSatelliteTemperature =
satelliteTemperatureData && getLatestData(satelliteTemperatureData);
const satelliteTemperature =
latestSatelliteTemperature && latestSatelliteTemperature.value;
// Get waves data if unavailable through a spotter
const significantWaveHeights =
spotterData.significantWaveHeight.length > 0
? spotterData.significantWaveHeight
: significantWaveHeightsRaw;
const minWaveHeight =
significantWaveHeights && getMin(significantWaveHeights);
const maxWaveHeight =
significantWaveHeights && getMax(significantWaveHeights);
const avgWaveHeight =
significantWaveHeights && getAverage(significantWaveHeights);
const meanDirectionWaves =
spotterData.waveMeanDirection.length > 0
? spotterData.waveMeanDirection
: waveMeanDirectionRaw;
const waveMeanDirection =
meanDirectionWaves && getAverage(meanDirectionWaves, true);
const meanPeriodWaves =
spotterData.waveMeanPeriod.length > 0
? spotterData.waveMeanPeriod
: waveMeanPeriodRaw;
const waveMeanPeriod = meanPeriodWaves && getAverage(meanPeriodWaves, true);
// Make sure that windVelocity10MeterEastward and windVelocity10MeterNorthward have the same length.
const modelWindCheck =
windVelocity10MeterEastward.length === windVelocity10MeterNorthward.length;
const windSpeedsRaw = modelWindCheck
? windVelocity10MeterEastward.map((eastValue, index) =>
getWindSpeed(eastValue, windVelocity10MeterNorthward[index]),
)
: [];
const windDirectionsRaw = modelWindCheck
? windVelocity10MeterEastward.map((eastValue, index) =>
getWindDirection(eastValue, windVelocity10MeterNorthward[index]),
)
: [];
// Get wind data if unavailable through a spotter
const windSpeeds = isEmpty(spotterData.windSpeed)
? windSpeedsRaw
: spotterData.windSpeed;
const windDirections = isEmpty(spotterData.windDirection)
? windDirectionsRaw
: spotterData.windDirection;
const minWindSpeed = windSpeeds && getMin(windSpeeds);
const maxWindSpeed = windSpeeds && getMax(windSpeeds);
const avgWindSpeed = windSpeeds && getAverage(windSpeeds);
const windDirection = windDirections && getAverage(windDirections, true);
const dailyAlertLevel = calculateAlertLevel(
maxMonthlyMean,
satelliteTemperature,
degreeHeatingDays?.value,
);
return {
site: { id: site.id },
date: endOfDate,
dailyAlertLevel,
minBottomTemperature,
maxBottomTemperature,
avgBottomTemperature,
topTemperature,
satelliteTemperature,
degreeHeatingDays: degreeHeatingDays?.value,
minWaveHeight,
maxWaveHeight,
avgWaveHeight,
waveMeanDirection,
waveMeanPeriod,
minWindSpeed,
maxWindSpeed,
avgWindSpeed,
windDirection,
};
}
Example #18
Source File: AircraftLayer.tsx From react-flight-tracker with MIT License | 4 votes |
AircraftLayer: React.FC<Props> = (props) => {
// Fields
const contextName: string = 'AircraftLayer'
// External hooks
const styleTheme = useTheme();
// States
const [featureCollection, setFeatureCollection] = useState<FeatureCollection | undefined>(undefined);
const [pathPredictions, setPathPredictions] = useState<Array<Feature<Point, GeoJsonProperties>>>([]);
// Contexts
const systemContext = useContext(SystemContext)
const geospatialService = systemContext.getService<IGeospatialService>('GeospatialService');
// Refs
const pathPredictionSubscriptionRef = useRef<string>('');
// Effects
useEffect(() => {
// Mount
if (geospatialService) {
// Get a register key for the subscription and save it as reference
const registerKey = geospatialService.onPathPredictionUpdated(contextName, handlePathPredictionUpdated);
pathPredictionSubscriptionRef.current = registerKey;
}
// Unmount
return () => {
if (geospatialService) {
geospatialService.stopPathPrediction();
// Get the register key from the reference to unsubscribe
const registerKey = pathPredictionSubscriptionRef.current;
geospatialService.offPathPredictionUpdated(registerKey);
}
}
}, []);
useEffect(() => {
createFeatureCollection(pathPredictions).then((featureCollection) => {
setFeatureCollection(featureCollection);
if (!geospatialService)
return;
if (props.stateVectors.states.length > 1000) {
geospatialService.stopPathPrediction();
}
else {
geospatialService.restartPathPrediction(props.stateVectors);
}
})
}, [props.stateVectors]);
useEffect(() => {
createFeatureCollection(pathPredictions).then((featureCollection) => {
setFeatureCollection(featureCollection);
})
}, [pathPredictions]);
const handlePathPredictionUpdated = (destinations: Array<Feature<Point, GeoJsonProperties>>) => {
setPathPredictions(destinations);
};
const createFeatureCollection = (pathPredictions: Array<Feature<Point, GeoJsonProperties>>) => {
return new Promise<FeatureCollection>((res, rej) => {
var featureCollection: FeatureCollection = {
type: 'FeatureCollection',
features: []
};
for (var stateVector of props.stateVectors.states) {
if (!stateVector.latitude) {
continue;
}
if (!stateVector.longitude) {
continue;
}
// Get the index
const index = props.stateVectors.states.indexOf(stateVector);
// Check for selection
var isSelected = false;
if (props.selectedAircraft)
isSelected = stateVector.icao24 === props.selectedAircraft.icao24;
// Get callsign
const callsign = stateVector.callsign ? stateVector.callsign : stateVector.icao24;
// Get altitude
var altitude = stateVector.geo_altitude;
if ((altitude === null) || (altitude < 0))
altitude = stateVector.baro_altitude;
if ((altitude === null) || (altitude < 0))
altitude = 0;
// Get velocity in km/h
const velocity = stateVector.velocity ? (stateVector.velocity * 3.6) : -1;
// Get true track
const trueTrack = stateVector.true_track ? stateVector.true_track : 0.0;
// Get vertical rate
const verticalRate = stateVector.vertical_rate ? stateVector.vertical_rate : 0.0;
// Get is on ground
const isOnGround = stateVector.on_ground;
// Claculate color
var color = getColor(altitude);
if (isOnGround)
color = styleTheme.palette.text.secondary;
if (isSelected)
color = styleTheme.palette.primary.main;
var properties: GeoJsonProperties = {
['iconName']: getIconName(isOnGround, verticalRate, altitude, trueTrack),
['rotation']: getRotation(trueTrack, verticalRate, altitude),
['color']: color,
['isSelected']: isSelected,
['icao24']: stateVector.icao24,
['callsign']: callsign,
['altitude']: getFormattedValue(altitude, 1) + " m",
['velocity']: getFormattedValue(velocity, 1) + " km/h"
}
// Setup WGS84 coordinates
var position: Position = [stateVector.longitude, stateVector.latitude];
if (pathPredictions.length > 0) {
const feature = pathPredictions.find(feature => feature.properties !== null && feature.properties['icao24']! === stateVector.icao24);
if (feature)
position = feature.geometry.coordinates;
}
var point: Point = {
type: 'Point',
coordinates: position
};
var feature: Feature<Point, GeoJsonProperties> = {
type: 'Feature',
id: `${index}.${stateVector.icao24}`,
geometry: point,
properties: properties
}
featureCollection.features.push(feature);
}
res(featureCollection);
});
};
const getText = (): string | Expression | StyleFunction => {
var text: string | Expression | StyleFunction = '';
const simpleText = ["get", "callsign"] as Expression
const detailedText = ['format',
["get", "callsign"], { "font-scale": 1.0 },
"\n", {},
["get", "altitude"], { "font-scale": 0.75, "text-color": styleTheme.palette.text.primary },
"\n", {},
["get", "velocity"], { "font-scale": 0.75, "text-color": styleTheme.palette.text.primary }
] as StyleFunction;
if (props.zoom && props.zoom > 7)
text = simpleText;
if (props.zoom && props.zoom > 9)
text = detailedText;
return text;
};
const getSymbolLayout = () => {
var showText = false;
if (props.zoom && props.zoom > 7)
showText = true;
var isconSize = 1.0;
if (props.zoom && props.zoom > 7)
isconSize = 1.3;
if (props.zoom && props.zoom > 9)
isconSize = 1.6;
const symbolLayout: SymbolLayout = {
"icon-image": ["get", "iconName"],
"icon-allow-overlap": true,
"icon-rotate": ["get", "rotation"],
"icon-size": isconSize,
"text-field": showText ? getText() : '',
"text-optional": true,
"text-allow-overlap": true,
"text-anchor": showText ? 'top' : 'center',
"text-offset": showText ? [0, 1] : [0, 0]
};
return symbolLayout;
};
const getSymbolPaint = () => {
var symbolPaint: SymbolPaint = {
"icon-color": ["get", "color"],
"text-color": ["get", "color"],
"text-halo-width": 2,
"text-halo-color": styleTheme.palette.background.default,
"text-halo-blur": 2
};
return symbolPaint;
};
if (!props.stateVectors.states)
return null;
return (
<Source
type="geojson"
data={featureCollection}>
<Layer
id={aircraftLayerId}
type='symbol'
source='geojson'
layout={getSymbolLayout()}
paint={getSymbolPaint()} />
</Source>
)
}