diff --git a/pdb-js/src/app/components/datepicker/date-picker.component.ts b/pdb-js/src/app/components/datepicker/date-picker.component.ts index 60b4caf..3544bd5 100644 --- a/pdb-js/src/app/components/datepicker/date-picker.component.ts +++ b/pdb-js/src/app/components/datepicker/date-picker.component.ts @@ -155,7 +155,7 @@ export class DatePickerComponent implements ControlValueAccessor { const hours = x.hours ? "-"+x.hours + "H" : ""; const minutes = x.minutes ? "-"+x.minutes + "m" : ""; - const timeRange = `B${years}${months}${days}${hours}${minutes}/Bm`; + const timeRange = `B${years}${months}${days}${hours}${minutes}/Bm`; const newValue = new DateValue("RELATIVE", timeRange, timeRange); this.setDateValue(newValue); diff --git a/pdb-js/src/app/plot-view/plot-view.component.ts b/pdb-js/src/app/plot-view/plot-view.component.ts index 1a74614..eeaeb09 100644 --- a/pdb-js/src/app/plot-view/plot-view.component.ts +++ b/pdb-js/src/app/plot-view/plot-view.component.ts @@ -7,6 +7,7 @@ import { Overlay } from "@angular/cdk/overlay"; import { DateTime, Duration } from "luxon"; import { DateValue } from '../components/datepicker/date-picker.component'; +import { Observable } from 'rxjs'; @Component({ selector: 'pdb-plot-view', @@ -190,11 +191,11 @@ export class PlotViewComponent { } zoomRange(range: SelectionRange) { - this.shiftDate(this.config?.dateRange.value!, range.startPercentOfDateRange, range.endPercentOfDateRange-1); + this.shiftDate(this.config?.dateRange!, range.startPercentOfDateRange, range.endPercentOfDateRange-1); } zoomWithDateAnchor(dateAnchor: DateAnchor){ - this.shiftDateByAnchor(this.config?.dateRange.value!, dateAnchor.cursorPercentOfDateRange, dateAnchor.zoomFactor); + this.shiftDateByAnchor(this.config?.dateRange!, dateAnchor.cursorPercentOfDateRange, dateAnchor.zoomFactor); } zoomByScroll(event: WheelEvent) { @@ -295,18 +296,27 @@ export class PlotViewComponent { * shiftDateByAnchor(dateRangeAsString, 0.20, 0.5) zooms in by 50%, so that the date that was at 20% before the zoom is still at 20% after the zoom * shiftDateByAnchor(dateRangeAsString, 0.33, 2) zooms out by 50%, so that the date that was at 33% before the zoom is still at 33% after the zoom */ - shiftDateByAnchor(dateRange:string, anchorInPercentOfDateRange:number, zoomFactor:number) + shiftDateByAnchor(dateValue:DateValue, anchorInPercentOfDateRange:number, zoomFactor:number) { - const dateRangeParsed = this.parseDateRange(dateRange); - const dateRangeInSeconds = Math.floor(dateRangeParsed.duration.toMillis()/1000); + debugger; + const dateRangeParsed = this.parseDateRange(dateValue); + dateRangeParsed.subscribe({ + next: (dataRange: DateRange) => { + const dateRangeInSeconds = Math.floor(dataRange.duration.toMillis()/1000); - const anchorTimestampInSeconds = dateRangeParsed.startDate.plus(Math.floor(dateRangeInSeconds*anchorInPercentOfDateRange)*1000); - const newDateRangeInSeconds = dateRangeInSeconds * zoomFactor; + const anchorTimestampInSeconds = dataRange.startDate.plus(Math.floor(dateRangeInSeconds*anchorInPercentOfDateRange)*1000); + const newDateRangeInSeconds = dateRangeInSeconds * zoomFactor; + + const newStartDate = anchorTimestampInSeconds.minus(newDateRangeInSeconds*anchorInPercentOfDateRange*1000); + const newEndDate = newStartDate.plus({seconds: newDateRangeInSeconds});; + + this.setDateRange(newStartDate, newEndDate); + }, + error: (err: any) => { + window.console.error("failed to parse DateValue into DateRange: ", err); + } + }) - const newStartDate = anchorTimestampInSeconds.minus(newDateRangeInSeconds*anchorInPercentOfDateRange*1000); - const newEndDate = newStartDate.plus({seconds: newDateRangeInSeconds});; - - this.setDateRange(newStartDate, newEndDate); } /** @@ -318,27 +328,36 @@ export class PlotViewComponent { * shiftDate(dateRangeAsString, -0.5, -0.5) will move the range by half its size to older values * shiftDate(dateRangeAsString, 1, 1) will move the range by its size to newer values */ - shiftDate(dateRange: string, factorStartDate: number, factorEndDate: number) + shiftDate(dateValue: DateValue, factorStartDate: number, factorEndDate: number) { - const dateRangeParsed = this.parseDateRange(dateRange); - const dateRangeInSeconds = Math.floor(dateRangeParsed.duration.toMillis()/1000); - - const newStartDate = dateRangeParsed.startDate.plus({seconds: dateRangeInSeconds*factorStartDate}); - const newEndDate = dateRangeParsed.endDate.plus({seconds: dateRangeInSeconds*factorEndDate}); - - this.setDateRange(newStartDate, newEndDate); + debugger; + this.parseDateRange(dateValue).subscribe( + dateRangeParsed => { + const dateRangeInSeconds = Math.floor(dateRangeParsed.duration.toMillis()/1000); + + const newStartDate = dateRangeParsed.startDate.plus({seconds: dateRangeInSeconds*factorStartDate}); + const newEndDate = dateRangeParsed.endDate.plus({seconds: dateRangeInSeconds*factorEndDate}); + + this.setDateRange(newStartDate, newEndDate); + } + ); } - parseDateRange(dateRangeAsString : string) : DateRange { - const startDate = DateTime.fromFormat(dateRangeAsString.slice(0, 19), this.DATE_PATTERN ); - const endDate = DateTime.fromFormat(dateRangeAsString.slice(22, 41), this.DATE_PATTERN ); - - - return { - startDate: startDate, - endDate: endDate, - duration: endDate.diff(startDate), - }; + parseDateRange(dateValue : DateValue) : Observable { + return this.service.toDateRange(dateValue); + /* + .pipe(map((dateRangeAsString:string) => { + const startDate = DateTime.fromFormat(dateRangeAsString.slice(0, 19), this.DATE_PATTERN ); + const endDate = DateTime.fromFormat(dateRangeAsString.slice(22, 41), this.DATE_PATTERN ); + + + return { + startDate: startDate, + endDate: endDate, + duration: endDate.diff(startDate), + }; + })); + */ } diff --git a/pdb-js/src/app/plot.service.ts b/pdb-js/src/app/plot.service.ts index f430967..81d00cb 100644 --- a/pdb-js/src/app/plot.service.ts +++ b/pdb-js/src/app/plot.service.ts @@ -3,11 +3,16 @@ import { HttpClient, HttpParams } from "@angular/common/http"; import { Observable } from "rxjs"; import { map } from "rxjs/operators"; import { DateValue } from "./components/datepicker/date-picker.component"; +import { DateRange } from "./plot-view/plot-view.component"; +import { DateTime } from "luxon"; @Injectable({ providedIn: "root", }) export class PlotService { + + readonly DATE_PATTERN = "yyyy-MM-dd'T'HH:mm:ss"; + plotTypes: Array; constructor(private http: HttpClient) { @@ -253,6 +258,20 @@ export class PlotService { ), ); } + toDateRange(dateValue: DateValue): Observable { + return this.http.post<{start: string, end:string, startEpochMilli: number, endEpochMilli: number}>("//" + window.location.hostname+":" + window.location.port +"/api/dates",dateValue) + .pipe(map((data) => { + const startDate = DateTime.fromFormat(data.start.slice(0, -1), this.DATE_PATTERN ); + const endDate = DateTime.fromFormat(data.end.slice(0, -1), this.DATE_PATTERN ); + + + return { + startDate: startDate, + endDate: endDate, + duration: endDate.diff(startDate), + }; + })); + } } export class PlotType { diff --git a/pdb-plotting/src/main/java/org/lucares/pdb/plot/api/DateTimeRangeParser.java b/pdb-plotting/src/main/java/org/lucares/pdb/plot/api/DateTimeRangeParser.java index 11ace4a..f2d13a3 100644 --- a/pdb-plotting/src/main/java/org/lucares/pdb/plot/api/DateTimeRangeParser.java +++ b/pdb-plotting/src/main/java/org/lucares/pdb/plot/api/DateTimeRangeParser.java @@ -1,16 +1,22 @@ package org.lucares.pdb.plot.api; import java.time.DayOfWeek; +import java.time.LocalDateTime; import java.time.OffsetDateTime; +import java.time.ZoneOffset; +import java.time.format.DateTimeFormatter; import java.time.temporal.ChronoUnit; import java.time.temporal.TemporalAdjusters; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.lucares.pdb.api.DateTimeRange; +import org.lucares.utils.Preconditions; public class DateTimeRangeParser { + private static final DateTimeFormatter DATE_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); + public static DateTimeRange parse(final OffsetDateTime offsetTime, final String datePeriod) { final String[] startEnd = datePeriod.split(Pattern.quote("/")); @@ -122,4 +128,16 @@ public class DateTimeRangeParser { throw new IllegalArgumentException("invalid input: " + timeDefinition); } + public static DateTimeRange parseAbsolute(final String dateRangeAsString) { + final String[] startEnd = dateRangeAsString.split(Pattern.quote(" - ")); + Preconditions.checkEqual(startEnd.length, 2, "invalid date range: ''{0}''", dateRangeAsString); + + final String startString = startEnd[0]; + final String endString = startEnd[1]; + + final OffsetDateTime start = LocalDateTime.parse(startString, DATE_FORMAT).atOffset(ZoneOffset.UTC); + final OffsetDateTime end = LocalDateTime.parse(endString, DATE_FORMAT).atOffset(ZoneOffset.UTC); + + return new DateTimeRange(start, end); + } } diff --git a/pdb-plotting/src/main/java/org/lucares/pdb/plot/api/PlotSettings.java b/pdb-plotting/src/main/java/org/lucares/pdb/plot/api/PlotSettings.java index 92e78d8..97aebf5 100644 --- a/pdb-plotting/src/main/java/org/lucares/pdb/plot/api/PlotSettings.java +++ b/pdb-plotting/src/main/java/org/lucares/pdb/plot/api/PlotSettings.java @@ -1,19 +1,15 @@ package org.lucares.pdb.plot.api; -import java.time.LocalDateTime; import java.time.OffsetDateTime; -import java.time.ZoneOffset; import java.time.format.DateTimeFormatter; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.TreeMap; -import java.util.regex.Pattern; import org.lucares.pdb.api.DateTimeRange; import org.lucares.recommind.logs.GnuplotAxis; import org.lucares.recommind.logs.GnuplotSettings; -import org.lucares.utils.Preconditions; public class PlotSettings { @@ -96,12 +92,7 @@ public class PlotSettings { final DateTimeRange dateTimeRange = DateTimeRangeParser.parse(OffsetDateTime.now(), dateValue.getValue()); return dateTimeRange; case ABSOLUTE: - final String[] startEnd = dateValue.getValue().split(Pattern.quote(" - ")); - Preconditions.checkEqual(startEnd.length, 2, "invalid date range: ''{0}''", dateValue); - - final OffsetDateTime startDate = LocalDateTime.parse(startEnd[0], DATE_FORMAT).atOffset(ZoneOffset.UTC); - final OffsetDateTime endDate = LocalDateTime.parse(startEnd[1], DATE_FORMAT).atOffset(ZoneOffset.UTC); - return new DateTimeRange(startDate, endDate); + return DateTimeRangeParser.parseAbsolute(dateValue.getValue()); } throw new UnsupportedOperationException(); diff --git a/pdb-ui/src/main/java/org/lucares/pdbui/PdbController.java b/pdb-ui/src/main/java/org/lucares/pdbui/PdbController.java index f42e812..6b44f9c 100644 --- a/pdb-ui/src/main/java/org/lucares/pdbui/PdbController.java +++ b/pdb-ui/src/main/java/org/lucares/pdbui/PdbController.java @@ -3,6 +3,8 @@ package org.lucares.pdbui; import java.io.IOException; import java.nio.file.Path; import java.text.Collator; +import java.time.OffsetDateTime; +import java.time.ZoneId; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; @@ -26,6 +28,9 @@ import org.lucares.pdb.api.DateTimeRange; import org.lucares.pdb.api.QueryWithCaretMarker; import org.lucares.pdb.api.QueryWithCaretMarker.ResultMode; import org.lucares.pdb.datastore.Proposal; +import org.lucares.pdb.plot.api.DateTimeRangeParser; +import org.lucares.pdb.plot.api.DateValue; +import org.lucares.pdb.plot.api.DateValue.DateType; import org.lucares.pdb.plot.api.PlotSettings; import org.lucares.pdbui.domain.AutocompleteProposal; import org.lucares.pdbui.domain.AutocompleteProposalByValue; @@ -345,6 +350,22 @@ public class PdbController implements HardcodedValues, PropertyKeys { return result; } + @RequestMapping(path = "/dates", // + method = RequestMethod.POST, // + consumes = MediaType.APPLICATION_JSON_VALUE, // + produces = MediaType.APPLICATION_JSON_VALUE // + ) + @ResponseBody + public DateTimeRange dates(@RequestBody final DateValue dateValue) { + final DateType type = dateValue.getType(); + final DateTimeRange result = switch (type) { + case RELATIVE -> DateTimeRangeParser.parse(OffsetDateTime.now(ZoneId.of("UTC")), dateValue.getValue()); + case QUICK -> DateTimeRangeParser.parse(OffsetDateTime.now(ZoneId.of("UTC")), dateValue.getValue()); + case ABSOLUTE -> DateTimeRangeParser.parseAbsolute(dateValue.getValue()); + }; + return result; + } + @PostMapping(path = "/data", consumes = MediaType.MULTIPART_MIXED_VALUE) @ResponseBody @ResponseStatus(code = HttpStatus.CREATED)