rdview-service
Version:
Rdview service for loading road photos
176 lines (143 loc) • 6.4 kB
text/typescript
import { Passage, Segment } from './interfaces';
import { RoadSegmentService } from './road-segment-service';
import { dateParser, sortPassagesByDateDesc, sortPhotosByKmAsc } from './utils';
export class PassageService {
public loadingPreviousSegment: Promise<void>;
public isPreviousSegmentEmpty: boolean;
public loadingNextSegment: Promise<void>;
public isNextSegmentEmpty: boolean;
private dateDiffInMsForSamePassage = 1000 * 30;
private rangeDiffInKmForSamePassage = .2;
private passageInitKmRange = 1;
private roadSegmentService: RoadSegmentService;
private segment: Segment;
constructor(settings: { apiUrl?: string, authorization: string }) {
this.roadSegmentService = new RoadSegmentService({
apiUrl: settings.apiUrl,
authorization: settings.authorization
});
}
public initByRoad(idRd: number, km: number): Promise<Segment> {
return this.roadSegmentService
.getSegmentByRoad(idRd, Math.max(0, km - this.passageInitKmRange),
km + this.passageInitKmRange)
.then(segment => this.initFirstSegment(segment));
}
public initByCoordinates(lat: number, lon: number): Promise<Segment> {
return this.roadSegmentService.getSegmentByCoordinates(lat, lon)
.then(segment => this.initFirstSegment(segment));
}
public getSegment(): Segment {
return JSON.parse(JSON.stringify(this.segment), dateParser);
}
public loadNextSegment() {
if (this.loadingNextSegment) {
return;
}
this.loadingNextSegment = this.roadSegmentService
.getSegmentByRoad(this.segment.road.id, this.segment.rdKmTo,
this.segment.rdKmTo + this.passageInitKmRange)
.then(segment => {
this.loadingNextSegment = null;
if (this.isSegmentWithPassages(segment)) {
this.includeSegment(segment);
} else {
this.isNextSegmentEmpty = true;
}
});
}
public loadPreviousSegment() {
if (this.loadingPreviousSegment) {
return;
}
this.loadingPreviousSegment = this.roadSegmentService
.getSegmentByRoad(this.segment.road.id,
Math.max(0, this.segment.rdKmFrom - this.passageInitKmRange), this.segment.rdKmFrom)
.then(segment => {
this.loadingPreviousSegment = null;
if (this.isSegmentWithPassages(segment)) {
this.includeSegment(segment);
} else {
this.isPreviousSegmentEmpty = true;
}
});
}
private initFirstSegment(segment: Segment): Segment {
if (!segment) {
return;
}
this.segment = segment;
this.segment.passages = sortPassagesByDateDesc(this.segment.passages);
this.segment.passages = this.getPassagesWithUpdatedKmBorders(this.segment.passages);
return segment;
}
private isSegmentWithPassages(segment: Segment): boolean {
return segment != null &&
Array.isArray(segment.passages) &&
segment.passages.length > 0;
}
private includeSegment(segment: Segment) {
if (!segment) {
return;
}
this.segment.rdKmFrom = Math.min(this.segment.rdKmFrom,
segment.rdKmFrom);
this.segment.rdKmTo = Math.max(this.segment.rdKmTo,
segment.rdKmTo);
segment.passages.forEach(newPassage => {
const existingNeighbourPassage = this.segment.passages
.find(existingPassage => {
if (existingPassage.direction !== newPassage.direction) {
return false;
}
if (!this.isPassagesNeighboursByKm(newPassage, existingPassage,
this.rangeDiffInKmForSamePassage)) {
return false;
}
return this.isPassagesNeighboursByDate(newPassage, existingPassage,
this.dateDiffInMsForSamePassage);
});
if (existingNeighbourPassage) {
existingNeighbourPassage.views = sortPhotosByKmAsc(
existingNeighbourPassage.views.concat(newPassage.views));
} else {
this.segment.passages.push(newPassage);
}
});
this.segment.passages = sortPassagesByDateDesc(this.segment.passages);
this.segment.passages = this.getPassagesWithUpdatedKmBorders(this.segment.passages);
}
private isPassagesNeighboursByKm(passage1: Passage, passage2: Passage, maxKmDiff: number): boolean {
const getPassagePhotoKmArray = (passage: Passage) => passage.views.map(photo => photo.rdKm);
const passage1PhotoKmArray = getPassagePhotoKmArray(passage1);
const passage2PhotoKmArray = getPassagePhotoKmArray(passage2);
return this.inNumberArraysIntersects(passage1PhotoKmArray, passage2PhotoKmArray, maxKmDiff);
}
private isPassagesNeighboursByDate(passage1: Passage, passage2: Passage, maxMsDiff: number): boolean {
const getPassagePhotoDateInMsArray = (passage: Passage) =>
passage.views.map(photo => photo.date.getTime());
const passage1PhotoDateInMsArray = getPassagePhotoDateInMsArray(passage1);
const passage2PhotoDateInMsArray = getPassagePhotoDateInMsArray(passage2);
return this.inNumberArraysIntersects(passage1PhotoDateInMsArray,
passage2PhotoDateInMsArray, maxMsDiff);
}
private inNumberArraysIntersects(array1: number[], array2: number[], precision: number) {
const minValueInArray1 = Math.min.apply(null, array1);
const minValueInArray2 = Math.min.apply(null, array2);
const maxValueInArray1 = Math.max.apply(null, array1);
const maxValueInArray2 = Math.max.apply(null, array2);
const isValueBetween = (value, borderLeft, borderRight, prec) =>
borderLeft - prec <= value && value <= borderRight + prec;
return isValueBetween(minValueInArray1, minValueInArray2, maxValueInArray2, precision) ||
isValueBetween(maxValueInArray1, minValueInArray2, maxValueInArray2, precision) ||
isValueBetween(minValueInArray2, minValueInArray1, maxValueInArray1, precision) ||
isValueBetween(maxValueInArray2, minValueInArray1, maxValueInArray1, precision);
}
private getPassagesWithUpdatedKmBorders(passages: Passage[]): Passage[] {
return passages.map(passage => {
passage.rdKmFrom = Math.min.apply(null, passage.views.map(photo => photo.rdKm));
passage.rdKmTo = Math.max.apply(null, passage.views.map(photo => photo.rdKm));
return passage;
});
}
}