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

Feat(intervals)(1133) #1287

Open
wants to merge 15 commits into
base: master
Choose a base branch
from
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ describe('SelectCaseAppEffects', () => {
const
orientation: CaseOrientation = 'Imagery Perspective',
geoFilter: CaseGeoFilter = CaseGeoFilter.PinPoint,
timeFilter: CaseTimeFilter = 'Start - End',
timeFilter: CaseTimeFilter = CaseTimeFilter.StartEnd,
time: CaseTimeState = { type: 'absolute', from: new Date(0), to: new Date(0) },
region: CaseRegionState = {},
dataInputFilters: CaseDataInputFiltersState = { fullyChecked: true, filters: [], active: true },
Expand Down
10 changes: 5 additions & 5 deletions src/app/@ansyn/ansyn/app-effects/effects/filters.app.effects.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,9 @@ import {
import 'rxjs/add/operator/share';
import 'rxjs/add/observable/of';
import { SetBadgeAction } from '@ansyn/menu/actions/menu.actions';
import { selectFavoriteOverlays } from '@ansyn/core/reducers/core.reducer';
import { selectFavoriteOverlays, selectOverlaysCriteria } from '@ansyn/core/reducers/core.reducer';
import { CaseFacetsState, CaseFilter, FilterType } from '@ansyn/core/models/case.model';
import { Overlay } from '@ansyn/core/models/overlay.model';
import { Overlay, OverlaysCriteria } from '@ansyn/core/models/overlay.model';
import { FilterMetadata } from '@ansyn/menu-items/filters/models/metadata/filter-metadata.interface';
import { FiltersService } from '@ansyn/menu-items/filters/services/filters.service';
import { FilterModel } from '@ansyn/core/models/filter.model';
Expand Down Expand Up @@ -69,10 +69,10 @@ export class FiltersAppEffects {
*/
@Effect()
updateOverlayFilters$ = this.onFiltersChanges$
.withLatestFrom(this.overlaysArray$)
.mergeMap(([[filters, showOnlyFavorite, favoriteOverlays], overlaysArray]: [[Filters, boolean, Overlay[]], Overlay[]]) => {
.withLatestFrom(this.overlaysArray$, this.store$.select(selectOverlaysCriteria))
.mergeMap(([[filters, showOnlyFavorite, favoriteOverlays], overlaysArray, { time }]: [[Filters, boolean, Overlay[]], Overlay[], OverlaysCriteria]) => {
const filterModels: FilterModel[] = FiltersService.pluckFilterModels(filters);
const filteredOverlays: string[] = OverlaysService.buildFilteredOverlays(overlaysArray, filterModels, favoriteOverlays, showOnlyFavorite);
const filteredOverlays: string[] = OverlaysService.buildFilteredOverlays(overlaysArray, filterModels, favoriteOverlays, showOnlyFavorite, time);
const message = (filteredOverlays && filteredOverlays.length) ? overlaysStatusMessages.nullify : overlaysStatusMessages.noOverLayMatchFilters;
return [
new SetFilteredOverlaysAction(filteredOverlays),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ const regionCoordinates = [
const fetchParams: any = {
limit: 250,
region: turf.geometry('Polygon', regionCoordinates),
timeRange: {start: new Date().toISOString(), end: new Date().toISOString()}
timeRange: [{start: new Date().toISOString(), end: new Date().toISOString()}]
};

const fetchParamsWithLimitZero: any = { ...fetchParams, limit: 0};
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
import { Observable } from 'rxjs/Observable';
import { Inject, Injectable, InjectionToken } from '@angular/core';
import {
BaseOverlaySourceProvider, DateRange, IFetchParams, OverlayFilter,
BaseOverlaySourceProvider, IFetchParams, OverlayFilter,
StartAndEndDate
} from '@ansyn/overlays/models/base-overlay-source-provider.model';
import { Overlay, OverlaysFetchData } from '@ansyn/core/models/overlay.model';
import { Feature, Polygon } from 'geojson';
import { LoggerService } from '@ansyn/core/services/logger.service';
import { area, intersect, difference } from '@turf/turf';
import { DataInputFilterValue } from '@ansyn/core/models/case.model';
import { IDateRange } from '@ansyn/core/models/time.model';

export interface FiltersList {
name: string,
dates: DateRange[]
dates: IDateRange[]
sensorNames: string[],
coverage: number[][][][]
}
Expand Down Expand Up @@ -142,7 +143,7 @@ export class MultipleOverlaysSourceProvider extends BaseOverlaySourceProvider {
errors,
data: null,
limited: -1
}
};
}

return this.mergeOverlaysFetchData(overlays, fetchParams.limit, errors);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,8 @@ export class OpenAerialSourceProvider extends BaseOverlaySourceProvider {
platform: 'uav',
limit: `${fetchParams.limit + 1}`,
bbox: `${bbox[0]},${bbox[1]},${bbox[2]},${bbox[3]}`,
acquisition_from: fetchParams.timeRange.start.toISOString(),
acquisition_to: fetchParams.timeRange.end.toISOString()
acquisition_from: fetchParams.timeRange[0].start.toISOString(),
acquisition_to: fetchParams.timeRange[0].end.toISOString()
};

return this.http.get<any>(baseUrl, { params: params })
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,11 +61,11 @@ export class PlanetSourceProvider extends BaseOverlaySourceProvider {
});
}

buildFilters(config: IPlanetFilter[], sensors?: string[]) {
buildFilters(config: IPlanetFilter[], sensors?: string[], filterType = 'AndFilter') {
return {
item_types: Array.isArray(sensors) ? sensors : this.planetOverlaysSourceConfig.itemTypes,
filter: {
type: 'AndFilter',
type: filterType,
config: config
}
};
Expand All @@ -86,10 +86,7 @@ export class PlanetSourceProvider extends BaseOverlaySourceProvider {
const limit = `${fetchParams.limit + 1}`;

const bboxFilter = { type: 'GeometryFilter', field_name: 'geometry', config: fetchParams.region };
const dateFilter = {
type: 'DateRangeFilter', field_name: 'acquired',
config: { gte: fetchParams.timeRange.start.toISOString(), lte: fetchParams.timeRange.end.toISOString() }
};
const dateFilter = this.buildDateFilter(fetchParams);

const filters: IPlanetFilter[] = [bboxFilter, dateFilter];

Expand Down Expand Up @@ -129,6 +126,39 @@ export class PlanetSourceProvider extends BaseOverlaySourceProvider {
});
}

// build date filter for single / multiple time ranges
buildDateFilter(fetchParams: IFetchParams) {
let dateFilter;
if (fetchParams.timeRange.length === 1) {
// single time range (like in start-end)
dateFilter = {
type: 'DateRangeFilter',
field_name: 'acquired',
config: {
gte: fetchParams.timeRange[0].start.toISOString(),
lte: fetchParams.timeRange[0].end.toISOString()
}
}
} else {
// multiple time ranges (like in intervals)
const dateRangeFilters = fetchParams.timeRange.map(tr => {
return {
type: 'DateRangeFilter',
field_name: 'acquired',
config: {
gte: tr.start.toISOString(),
lte: tr.end.toISOString()
}
}
});
dateFilter = {
type: 'OrFilter',
config: dateRangeFilters
};
}
return dateFilter;
}

getById(id: string, sourceType: string): Observable<Overlay> {
const baseUrl = this.planetOverlaysSourceConfig.baseUrl;
const body = this.buildFilters([{ type: 'StringInFilter', field_name: 'id', config: [id] }]);
Expand Down
6 changes: 0 additions & 6 deletions src/app/@ansyn/assets/styles/styles.less
Original file line number Diff line number Diff line change
Expand Up @@ -180,12 +180,6 @@ button[disabled] {
}
}

input[type=number]::-webkit-inner-spin-button,
input[type=number]::-webkit-outer-spin-button {
-webkit-appearance: none;
margin: 0;
}

@font-face {
font-family: 'AnSyn';
src: url('/assets/fonts/AnSyn/AnSyn.eot?mxpept&v=1');
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
<input type="date" #datePicker [disabled]="disabled">
<i class="icon-status-bar-time"></i>
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
@import "../../less/common";

:host {
position: relative;
input.flatpickr-input {
width: 130px;
height: 20px;
padding: 0 5px;
}
i {
position: absolute;
right: 10px;
top: 4px;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';

import { DatePickerComponent } from './ansyn-date-picker.component';


describe('DatePickerComponent', () => {
let component: DatePickerComponent;
let fixture: ComponentFixture<DatePickerComponent>;

beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [DatePickerComponent]
})
.compileComponents();
}));

beforeEach(() => {
fixture = TestBed.createComponent(DatePickerComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});

it('should be created', () => {
expect(component).toBeTruthy();
});
});

Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import { Component, ElementRef, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core';
import * as flatpickr from 'flatpickr';

@Component({
selector: 'ansyn-date-picker',
templateUrl: './ansyn-date-picker.component.html',
styleUrls: ['./ansyn-date-picker.component.less']
})

export class DatePickerComponent implements OnInit {

private _datePickerValue: Date;
private _datePickerInstance: any;

@ViewChild('datePicker') datePicker: ElementRef;
@Output() dateChange = new EventEmitter<Date>();

@Input() disabled = false;

@Input()
set date(val) {
this._datePickerValue = val;
if (this._datePickerInstance) {
this._datePickerInstance.setDate(this._datePickerValue, false);
}
}

constructor() {}

ngOnInit() {
// see API: https://flatpickr.js.org/options/
this._datePickerInstance = new (<any>flatpickr)(this.datePicker.nativeElement, {
// id: 'end',
time_24hr: true,
enableTime: true,
dateFormat: 'H:i d/m/Y',
defaultDate: this._datePickerValue ? this._datePickerValue : new Date(),
onChange: this.onDateChange.bind(this),
plugins: [this.confirmDatePlugin({})]
});
}

confirmDatePlugin(pluginConfig: any) {
const defaultConfig = {
confirmIcon: '<svg version=\'1.1\' xmlns=\'http://www.w3.org/2000/svg\' xmlns:xlink=\'http://www.w3.org/1999/xlink\' width=\'17\' height=\'17\' viewBox=\'0 0 17 17\'> <g> </g> <path d=\'M15.418 1.774l-8.833 13.485-4.918-4.386 0.666-0.746 4.051 3.614 8.198-12.515 0.836 0.548z\' fill=\'#000000\' /> </svg>',
confirmText: 'OK ',
showAlways: false,
theme: 'light'
};

const config = Object.assign(defaultConfig, pluginConfig);

return function (fp) {
const hooks = {
onKeyDown: function onKeyDown(_, __, ___, e) {
if (fp.config.enableTime && e.key === 'Tab' && e.target === fp.amPM) {
e.preventDefault();
fp.confirmContainer.focus();
} else if (e.key === 'Enter' && e.target === fp.confirmContainer) {
fp.close();
}
},
onReady: function onReady() {
if (fp.calendarContainer === undefined) {
return;
}

fp.confirmContainer = fp._createElement('div', 'flatpickr-confirm ' + (config.showAlways ? 'visible' : '') + ' ' + config.theme + 'Theme', config.confirmText);

fp.confirmContainer.tabIndex = -1;
fp.confirmContainer.innerHTML += config.confirmIcon;

fp.confirmContainer.addEventListener('click', fp.close);
fp.calendarContainer.appendChild(fp.confirmContainer);
}
} as any;

if (!config.showAlways) {
hooks.onChange = function (dateObj, dateStr) {
const showCondition = fp.config.enableTime || fp.config.mode === 'multiple';
if (dateStr && !fp.config.inline && showCondition) {
return fp.confirmContainer.classList.add('visible');
}
fp.confirmContainer.classList.remove('visible');
};
}

return hooks;
};
}

onDateChange(event) {
if (this.disabled) {
return false;
}
this._datePickerValue = event[0];
this.dateChange.emit(this._datePickerValue);
}
}
2 changes: 2 additions & 0 deletions src/app/@ansyn/core/core.module.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { AnsynCheckboxComponent } from './components/ansyn-checkbox/ansyn-checkbox.component';
import { DatePickerComponent } from './components/ansyn-date-picker/ansyn-date-picker.component';
import { ImageryStatusComponent } from './components/imagery-status/imagery-status.component';
import { PlaceholderComponent } from './components/placeholder/placeholder.component';
import { StoreModule } from '@ngrx/store';
Expand All @@ -18,6 +19,7 @@ import { StorageService } from '@ansyn/core/services/storage/storage.service';

const coreComponents = [
AnsynCheckboxComponent,
DatePickerComponent,
ImageryStatusComponent,
PlaceholderComponent,
ToastComponent,
Expand Down
1 change: 1 addition & 0 deletions src/app/@ansyn/core/less/common.less
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
@overlays-dots-color-hover: #bd0fe2;
@menu-width: 90px;
@menu-color: #2E2D46;
@text-disabled-color: #9D9D9D;
@timeline-area-height: 110px;

.default-menu-item {
Expand Down
19 changes: 18 additions & 1 deletion src/app/@ansyn/core/models/case.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,11 @@ export interface IContextEntity extends IVisualizerEntity {
}

export type CaseOrientation = 'Align North' | 'User Perspective' | 'Imagery Perspective';
export type CaseTimeFilter = 'Start - End';

export enum CaseTimeFilter {
'StartEnd' = 'Start - End',
'Intervals' = 'Intervals'
}

export enum CaseGeoFilter {
PinPoint = 'Point',
Expand Down Expand Up @@ -83,10 +86,24 @@ export interface CaseDataInputFiltersState {
active: boolean
}

export interface TimeIntervals {
interval: number // in milliseconds
criteria: CaseIntervalCriteria
}

export interface CaseTimeState {
type: 'absolute',
from: Date,
to: Date
intervals?: TimeIntervals;
}

export type CaseIntervalCriteriaType = 'best' | 'closest-before' | 'closest-after' | 'closest-both';

export interface CaseIntervalCriteria {
type: CaseIntervalCriteriaType,
before?: number,
after?: number,
}

export interface CaseBooleanFilterMetadata {
Expand Down
22 changes: 22 additions & 0 deletions src/app/@ansyn/core/models/intervals.model.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { Overlay } from '@ansyn/core/models/overlay.model';

export interface IntervalTimeFrame {
startDate: Date;
endDate: Date;
span: number;
intervalsCount: number;
}

export interface IntervalTimeFrame {
startDate: Date;
endDate: Date;
span: number;
intervalsCount: number;
}

export interface Interval {
startTime: Date;
endTime: Date;
pivot: Date;
overlays: Array<Overlay>;
}
Loading