Skip to content

Commit

Permalink
feat: Add path endpoints to stops array
Browse files Browse the repository at this point in the history
Closes: #209
  • Loading branch information
petrot committed May 17, 2018
1 parent 318580b commit 92a2af3
Show file tree
Hide file tree
Showing 8 changed files with 123 additions and 60 deletions.
4 changes: 3 additions & 1 deletion src/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,8 @@ import {
OsmRoutePoiService,
GooglePoiService,
ReverseGeocodingService,
LanguageService
LanguageService,
HikeProgramService
} from './shared/services';
// Vendor
import { AngularFireModule } from 'angularfire2';
Expand Down Expand Up @@ -177,6 +178,7 @@ export class CustomRouterStateSerializer implements RouterStateSerializer<Router
OsmRoutePoiService,
GooglePoiService,
LanguageService,
HikeProgramService,
// Selectors
EditedGTrackPoiSelectors,
EditedHikeProgramSelectors,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ export class HikeEditPoisGTrackComponent implements OnInit, OnDestroy {
});

this.pois$
.debounceTime(150)
.debounceTime(200)
.takeUntil(this._destroy$)
.subscribe((pois: IGTrackPoi[]) => {
// Refresh markers
Expand Down
71 changes: 15 additions & 56 deletions src/app/pages/hike-edit/components/hike-edit-pois-hike/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,19 @@ import { Component, OnInit, OnDestroy } from '@angular/core';
import { Store } from '@ngrx/store';
import { Observable } from 'rxjs/Observable';
import { Subject } from 'rxjs/Subject';
import {
PoiSelectors, CenterRadius, GeometryService, GeoSearchSelectors, Poi,
PoiSaved, IGeoSearchContextState, RouteSelectors, ElevationService, ISegment, GameRuleService
} from 'subrepos/gtrack-common-ngx';
import { PoiSelectors, GeoSearchSelectors, Poi } from 'subrepos/gtrack-common-ngx';
import { GeospatialService } from 'subrepos/gtrack-common-ngx/app/shared/services/geospatial';
import { IPoiStored, IPoi, IHikeProgramStop } from 'subrepos/provider-client';
import { AdminMap, AdminMapService, AdminMapMarker } from 'app/shared/services/admin-map';
import { PoiEditorService } from 'app/shared/services';
import { PoiEditorService, HikeProgramService } from 'app/shared/services';
import { IGTrackPoi } from 'app/shared/interfaces';
import {
State, hikeEditPoiActions, IExternalPoiListContextState, commonPoiActions, commonGeoSearchActions, IHikeEditRoutePlannerState, editedHikeProgramActions
} from 'app/store';
import { State, hikeEditPoiActions, commonPoiActions } from 'app/store';
import {
HikeEditPoiSelectors, HikeEditMapSelectors, HikeEditRoutePlannerSelectors, EditedHikeProgramSelectors
} from 'app/store/selectors';

import * as _ from 'lodash';
import * as uuid from 'uuid/v1';
import * as turf from '@turf/turf';

@Component({
selector: 'gt-hike-edit-pois-hike',
Expand All @@ -42,10 +36,7 @@ export class HikeEditPoisHikeComponent implements OnInit, OnDestroy {
private _hikeEditPoiSelectors: HikeEditPoiSelectors,
private _hikeEditRoutePlannerSelectors: HikeEditRoutePlannerSelectors,
private _geoSearchSelectors: GeoSearchSelectors,
private _geometryService: GeometryService,
private _geospatialService: GeospatialService,
private _elevationService: ElevationService,
private _gameRuleService: GameRuleService,
private _hikeProgramService: HikeProgramService,
private _poiSelectors: PoiSelectors
) {}

Expand All @@ -64,7 +55,7 @@ export class HikeEditPoisHikeComponent implements OnInit, OnDestroy {
this._store.select(this._editedHikeProgramSelectors.getPoiIds).takeUntil(this._destroy$),
this._store.select(this._poiSelectors.getPoiIds).takeUntil(this._destroy$)
)
.debounceTime(150)
.debounceTime(200)
.takeUntil(this._destroy$)
.subscribe(([inHikePoiIds, inStorePoiIds]: [string[], string[]]) => {
const missingPoiIds = _.difference(inHikePoiIds, _.intersection(inHikePoiIds, inStorePoiIds))
Expand All @@ -78,6 +69,7 @@ export class HikeEditPoisHikeComponent implements OnInit, OnDestroy {
// Poi list
this.pois$ = this._store
.select(this._editedHikeProgramSelectors.getHikePois<IPoiStored>(this._poiSelectors.getAllPois))
.debounceTime(200)
.takeUntil(this._destroy$)
.filter((pois: IPoiStored[]) => typeof pois !== 'undefined')
.switchMap((pois: IPoiStored[]) => {
Expand All @@ -91,7 +83,7 @@ export class HikeEditPoisHikeComponent implements OnInit, OnDestroy {
});

this.pois$
.debounceTime(150)
.debounceTime(200)
.takeUntil(this._destroy$)
.subscribe((pois: IGTrackPoi[]) => {
// Refresh markers
Expand All @@ -102,14 +94,14 @@ export class HikeEditPoisHikeComponent implements OnInit, OnDestroy {
.select(this._editedHikeProgramSelectors.getStopsCount)
.takeUntil(this._destroy$)
.subscribe((stopsCount: number) => {
if (stopsCount > 0) {
this._store
.select(this._editedHikeProgramSelectors.getStops)
.take(1)
.subscribe((stops: IHikeProgramStop[]) => {
this._updateStopsSegment(_.orderBy(_.cloneDeep(stops), ['distanceFromOrigo']));
});
}
this._hikeProgramService.updateHikeProgramStops();
});

this._store
.select(this._hikeEditRoutePlannerSelectors.getPathLength)
.takeUntil(this._destroy$)
.subscribe((pathLength: number) => {
this._hikeProgramService.updateHikeProgramStops();
});

//
Expand Down Expand Up @@ -159,37 +151,4 @@ export class HikeEditPoisHikeComponent implements OnInit, OnDestroy {
public toggleOffrouteMarkers() {
this._store.dispatch(new hikeEditPoiActions.ToggleOffrouteMarkers('hike'));
}

/**
* Update stops' segment info
*/
private _updateStopsSegment(stops: IHikeProgramStop[]) {
this._store
.select(this._hikeEditRoutePlannerSelectors.getPath)
.take(1)
.subscribe(path => {
if (path.geometry.coordinates.length > 0) {
let _segmentStartPoint = path.geometry.coordinates[0];

for (const stop of stops) {
const _segmentEndPoint = [stop.lon, stop.lat];
const _segmentPath = this._geospatialService.snappedLineSlice(_segmentStartPoint, _segmentEndPoint, path);
const _segmentDistance = 1000 * turf.lineDistance(_segmentPath, {units: 'kilometers'});

stop.segment = {
uphill: this._elevationService.calculateUphill((<any>_segmentPath).geometry.coordinates),
downhill: this._elevationService.calculateDownhill((<any>_segmentPath).geometry.coordinates),
distance: _segmentDistance
}
stop.segment.time = this._gameRuleService.segmentTime(_segmentDistance, stop.segment.uphill),
stop.segment.score = this._gameRuleService.score(_segmentDistance, stop.segment.uphill)

// Save coords for the next segment
_segmentStartPoint = [stop.lon, stop.lat];
}

this._store.dispatch(new editedHikeProgramActions.SetStops(stops));
}
});
}
}
96 changes: 96 additions & 0 deletions src/app/shared/services/hike/hike-program.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import { Store } from '@ngrx/store';
import { State, editedHikeProgramActions } from 'app/store';
import { EditedHikeProgramSelectors, HikeEditRoutePlannerSelectors } from 'app/store/selectors';
import { IHikeProgramStop } from 'subrepos/provider-client';
import { GeospatialService } from 'subrepos/gtrack-common-ngx/app/shared/services/geospatial';
import { ElevationService, GameRuleService } from 'subrepos/gtrack-common-ngx';

import * as _ from 'lodash';
import * as turf from '@turf/turf';

@Injectable()
export class HikeProgramService {
constructor(
private _store: Store<State>,
private _geospatialService: GeospatialService,
private _elevationService: ElevationService,
private _gameRuleService: GameRuleService,
private _editedHikeProgramSelectors: EditedHikeProgramSelectors,
private _hikeEditRoutePlannerSelectors: HikeEditRoutePlannerSelectors,
) {}

/**
* Update stop segments and start/end points
*/
public updateHikeProgramStops() {
Observable
.combineLatest(
this._store.select(this._editedHikeProgramSelectors.getStops).take(1),
this._store.select(this._hikeEditRoutePlannerSelectors.getPath).take(1)
)
.take(1)
.subscribe(([stops, path]: [IHikeProgramStop[], any]) => {
const poiStops = _.cloneDeep(stops).filter(stop => stop.poiId);
console.log('poiStops', poiStops);
if (path.geometry.coordinates.length > 0) {
poiStops.unshift(this._createStopFromPathEndPoint(path, 0));
poiStops.push(this._createStopFromPathEndPoint(path, path.geometry.coordinates.length - 1));
}
this._updateStopsSegment(_.orderBy(poiStops, ['distanceFromOrigo']), path);
});
}

/**
* Create begin/end stop from path endpoints
*/
private _createStopFromPathEndPoint(path, coordIdx) {
const coord = path.geometry.coordinates[coordIdx];
return {
distanceFromOrigo: coord === 0 ? 0 : this._geospatialService.distanceOnLine(
path.geometry.coordinates[0],
coord,
path
),
onRoute: true,
lat: coord[1],
lon: coord[0],
segment: {
uphill: 0,
downhill: 0,
distance: 0,
score: 0,
time: 0
}
}
}

/**
* Update stops' segment info
*/
private _updateStopsSegment(stops: IHikeProgramStop[], path: any) {
if (_.get(path, 'geometry.coordinates', []).length > 0) {
let _segmentStartPoint = path.geometry.coordinates[0];
console.log('_segmentStartPoint', _segmentStartPoint);
for (const stop of stops) {
const _segmentEndPoint = [stop.lon, stop.lat];
const _segmentPath = this._geospatialService.snappedLineSlice(_segmentStartPoint, _segmentEndPoint, path);
const _segmentDistance = 1000 * turf.lineDistance(_segmentPath, {units: 'kilometers'});

stop.segment = {
uphill: this._elevationService.calculateUphill((<any>_segmentPath).geometry.coordinates),
downhill: this._elevationService.calculateDownhill((<any>_segmentPath).geometry.coordinates),
distance: _segmentDistance
}
stop.segment.time = this._gameRuleService.segmentTime(_segmentDistance, stop.segment.uphill),
stop.segment.score = this._gameRuleService.score(_segmentDistance, stop.segment.uphill)

// Save coords for the next segment
_segmentStartPoint = [stop.lon, stop.lat];
}

this._store.dispatch(new editedHikeProgramActions.SetStops(stops));
}
}
}
3 changes: 2 additions & 1 deletion src/app/shared/services/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export * from './hike-data/reverse-geocoding.service';
export * from './hike/reverse-geocoding.service';
export * from './hike/hike-program.service';
export * from './admin-map/admin-map.service';
export * from './poi';
export * from './language.service';
5 changes: 5 additions & 0 deletions src/app/store/selectors/hike-edit-route-planner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export class HikeEditRoutePlannerSelectors {
public getRoutePlanner: MemoizedSelector<object, IHikeEditRoutePlannerState>;
public getRoute: MemoizedSelector<object, any>;
public getPath: MemoizedSelector<object, any>;
public getPathLength: MemoizedSelector<object, number>;
public getSegments: MemoizedSelector<object, ISegment[]>;
public getTotal: MemoizedSelector<object, any>;
public getIsRoundTrip: MemoizedSelector<object, boolean>;
Expand All @@ -33,6 +34,10 @@ export class HikeEditRoutePlannerSelectors {
(state: IHikeEditRoutePlannerState) => state.route.features[0]
);

this.getPathLength = createSelector(this._featureSelector,
(state: IHikeEditRoutePlannerState) => _.get(state.route.features[0], 'geometry.coordinates', []).length
);

this.getSegments = createSelector(this._featureSelector,
(state: IHikeEditRoutePlannerState) => state.segments
);
Expand Down
2 changes: 1 addition & 1 deletion src/subrepos/provider-client/interfaces/hike-program.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export interface IRouteSegment {

export interface IHikeProgramStop {
distanceFromOrigo: number;
poiId: string;
poiId?: string;
lat: number;
lon: number;
onRoute?: boolean;
Expand Down

0 comments on commit 92a2af3

Please sign in to comment.