Skip to content

Commit

Permalink
polish: take in account code review
Browse files Browse the repository at this point in the history
  • Loading branch information
flyingtof committed May 29, 2024
1 parent 3e47dae commit a0e5e63
Show file tree
Hide file tree
Showing 14 changed files with 434 additions and 426 deletions.
4 changes: 4 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,7 @@ For the moment, it does not work with docker compose. But if you install the clo
- open another shell.
- define the required environment variables:`eval $(gcloud beta emulators datastore --data-dir=MY_DATA_DIR env-init)`
- you can then run the application locally in this shell with `npm run dev`

## Before you submit your PR:

run this command to format/lint/test: `npx nx check`
71 changes: 15 additions & 56 deletions apps/fxc-front/src/app/components/2d/path-element.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,12 @@ import { FaiSectors } from '../../gm/fai-sectors';
import { addAltitude } from '../../logic/elevation';
import { getCurrentUrl, pushCurrentState } from '../../logic/history';
import { drawRoute } from '../../logic/messages';
import { CircuitType, Score } from '../../logic/score/scorer';
import { CircuitType, getCircuitType, Score } from '../../logic/score/scorer';
import { setDistance, setEnabled, setRoute, setScore } from '../../redux/planner-slice';
import { RootState, store } from '../../redux/store';
import { PlannerElement } from './planner-element';
import { OptimizationResult, optimize, ScoringRules, ScoringTrack } from 'optimizer';
import { LeagueCode } from '../../logic/score/league/leagues';
import { getOptimizer, ScoringTrack } from 'optimizer';
import { getScoringRules } from '../../logic/score/league/leagues';

// Route color by circuit type.
const ROUTE_STROKE_COLORS = {
Expand Down Expand Up @@ -225,10 +225,10 @@ export class PathElement extends connect(store)(LitElement) {
this.closingSector.addListener('rightclick', (e) => this.appendToPath(e.latLng));
}

if (score.closingRadius) {
if (score.closingRadiusM) {
const center = points[score.indexes[0]];
this.closingSector.center = center;
this.closingSector.radius = score.closingRadius;
this.closingSector.radius = score.closingRadiusM;
this.closingSector.update();
this.closingSector.setMap(this.map);
} else {
Expand All @@ -252,67 +252,26 @@ export class PathElement extends connect(store)(LitElement) {

private computeScore(points: LatLon[]): Score {
const track: ScoringTrack = {
points: points.map((point, i) => {
return {
...point,
alt: 0,
timeSec: i * 60,
};
}),
minTimeSec: new Date().getTime() / 1000,
points: points.map((point, i) => ({ ...point, alt: 0, timeSec: i * 60 })),
startTimeSec: Math.round(new Date().getTime() / 1000),
};
const result = optimize({ track }, this.getLeague()).next().value;
const score = new Score({
circuit: this.getCircuitType(result),
distance: result.lengthKm * 1000,
const result = getOptimizer({ track }, getScoringRules(this.league)).next().value;
return new Score({
circuit: getCircuitType(result.circuit),
distanceM: result.lengthKm * 1000,
multiplier: result.multiplier,
closingRadius: result.closingRadius ? result.closingRadius * 1000 : null,
closingRadiusM: result.closingRadius ? result.closingRadius * 1000 : null,
indexes: result.solutionIndices,
points: result.score,
});
// force the score as computed because of an unwanted side effect in constructor.
score.forcePoints(result.score);
return score;
}

private getLeague(): ScoringRules {
switch (this.league as LeagueCode) {
case 'czl':
return ScoringRules.CzechLocal;
case 'cze':
return ScoringRules.CzechEuropean;
case 'czo':
return ScoringRules.CzechOutsideEurope;
case 'fr':
return ScoringRules.FederationFrancaiseVolLibre;
case 'leo':
return ScoringRules.Leonardo;
case 'nor':
return ScoringRules.Norway;
case 'ukc':
return ScoringRules.UnitedKingdomClub;
case 'uki':
return ScoringRules.UnitedKingdomInternational;
case 'ukn':
return ScoringRules.UnitedKingdomNational;
case 'xc':
return ScoringRules.XContest;
case 'xcppg':
return ScoringRules.XContestPPG;
case 'wxc':
return ScoringRules.WorldXC;
}
}

private getCircuitType(result: OptimizationResult) {
return result.circuit as unknown as CircuitType;
}

// Sends a message to the iframe host with the changes.
private postScoreToHost(score: Score) {
let kms = '';
let circuit = '';
if (score.distance && window.parent) {
kms = (score.distance / 1000).toFixed(1);
if (score.distanceM && window.parent) {
kms = (score.distanceM / 1000).toFixed(1);
circuit = CIRCUIT_SHORT_NAME[score.circuit];
if (score.circuit == CircuitType.OpenDistance) {
circuit += score.indexes.length - 2;
Expand Down
2 changes: 1 addition & 1 deletion apps/fxc-front/src/app/components/2d/planner-element.ts
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ export class PlannerElement extends connect(store)(LitElement) {
<div>
<div>${this.score.circuit}</div>
<div class="large">
${unsafeHTML(units.formatUnit(this.score.distance / 1000, this.units.distance, undefined, 'unit'))}
${unsafeHTML(units.formatUnit(this.score.distanceM / 1000, this.units.distance, undefined, 'unit'))}
</div>
</div>
<div class="collapsible">
Expand Down
41 changes: 36 additions & 5 deletions apps/fxc-front/src/app/logic/score/league/leagues.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
export const LEAGUES: { [name: string]: string } = {
import { ScoringRules } from 'optimizer';

// allowed league codes
export const leagueCodes = ['czl', 'cze', 'czo', 'fr', 'leo', 'nor', 'ukc', 'uki', 'ukn', 'xc', 'xcppg', 'wxc'];
export type LeagueCode = (typeof leagueCodes)[number];

export const LEAGUES: Readonly<Record<LeagueCode, string>> = {
czl: 'Czech (ČPP local)',
cze: 'Czech (ČPP Europe)',
czo: 'Czech (ČPP outside Europe)',
Expand All @@ -13,7 +19,32 @@ export const LEAGUES: { [name: string]: string } = {
wxc: 'World XC Online Contest',
};

// allowed league codes
// ensure that all league codes defined in each League sub classes are in this
// closed set.
export type LeagueCode = 'czl' | 'cze' | 'czo' | 'fr' | 'leo' | 'nor' | 'ukc' | 'uki' | 'ukn' | 'xc' | 'xcppg' | 'wxc';
export function getScoringRules(league: LeagueCode): ScoringRules {
switch (league) {
case 'czl':
return 'CzechLocal';
case 'cze':
return 'CzechEuropean';
case 'czo':
return 'CzechOutsideEurope';
case 'fr':
return 'FederationFrancaiseVolLibre';
case 'leo':
return 'Leonardo';
case 'nor':
return 'Norway';
case 'ukc':
return 'UnitedKingdomClub';
case 'uki':
return 'UnitedKingdomInternational';
case 'ukn':
return 'UnitedKingdomNational';
case 'xc':
return 'XContest';
case 'xcppg':
return 'XContestPPG';
case 'wxc':
return 'WorldXC';
}
throw Error('no corresponding rule for ' + league);
}
27 changes: 15 additions & 12 deletions apps/fxc-front/src/app/logic/score/scorer.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,30 @@
import { CircuitType as OptimizerCircuitType } from 'optimizer';

export enum CircuitType {
OpenDistance = 'Open distance',
FlatTriangle = 'Flat triangle',
FaiTriangle = 'Fai triangle',
OutAndReturn = 'Out and return',
}

export function getCircuitType(circuit?: OptimizerCircuitType) {
return circuit as unknown as CircuitType;
}

export class Score {
distance: number;
distanceM: number;
indexes: number[];
multiplier: number;
circuit: CircuitType;
closingRadius: number | null;
closingRadiusM: number | null;
points: number;

constructor(score: Partial<Omit<Score,'points'>>) {
this.distance = score.distance || 0;
this.indexes = score.indexes || [];
this.multiplier = score.multiplier || 1;
this.circuit = score.circuit || CircuitType.OpenDistance;
this.closingRadius = score.closingRadius || null;
this.points = (this.distance * this.multiplier) / 1000;
}
public forcePoints(points: number){
this.points = points;
constructor(score: Partial<Score>) {
this.distanceM = score.distanceM ?? 0;
this.indexes = score.indexes ?? [];
this.multiplier = score.multiplier ?? 1;
this.circuit = score.circuit ?? CircuitType.OpenDistance;
this.closingRadiusM = score.closingRadiusM ?? null;
this.points = score.points ?? 0;
}
}
3 changes: 2 additions & 1 deletion apps/fxc-front/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,8 @@ export default defineConfig({
define: {
__BUILD_TIMESTAMP__: JSON.stringify(format(new Date(), 'yyyyMMdd.HHmm')),
__AIRSPACE_DATE__: JSON.stringify(getAirspaceDate()),
global: {}, // required by igc-xc-score
// required by igc-xc-score. TODO(vicb): check how to remove this
global: {},
},
});

Expand Down
7 changes: 4 additions & 3 deletions libs/optimizer/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,17 @@
This library was generated with [Nx](https://nx.dev).

## Description

This library computes scores for flights using applicable rules of various XC leagues.

## Usage

The `src/lib/optimizer.ts#optimize` function computes score of a given track given by a `ScoringTrack` for a given league known by it's `LeagueCode`.
You can specify `OptimizationOptions` to limit either the number of the iterations performed during the optimization (`OptimizationOptions.maxLoop`)
You can specify `OptimizationOptions` to limit either the number of the iterations performed during the optimization (`OptimizationOptions.maxLoop`)
or the maximum duration in milliseconds allowed for the optimization.

The `optimize` function is a generator function. It returns an `Iterator<OptimizationResult,OptimizationResult>`. You should call the `next()`
method of this iterator to get the current `IteratorResult<OptimizationResult, OptimizationResult>`. The `value` property of the `IteratorResult`
The `optimize` function is a generator function. It returns an `Iterator<OptimizationResult,OptimizationResult>`. You should call the `next()`
method of this iterator to get the current `IteratorResult<OptimizationResult, OptimizationResult>`. The `value` property of the `IteratorResult`
gives the current `OptimizationResult` and the `done` property of the `IteratorResult` indicates if the optimization is terminated or not.

If the `done` property is false, you should call again the `next()` method of the iterator so that you get another result that should be a better
Expand Down
6 changes: 3 additions & 3 deletions libs/optimizer/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
export { optimize } from './lib/optimizer';
export { getOptimizer } from './lib/optimizer';
export type {
LatLonAltTime,
OptimizedCircuitType,
CircuitType,
ScoringTrack,
OptimizationResult,
OptimizationOptions,
OptimizationRequest,
} from './lib/optimizer';
export { ScoringRules } from './lib/scoringRules';
export type { ScoringRules } from './lib/scoringRules';
Loading

0 comments on commit a0e5e63

Please sign in to comment.