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

Add OpenHistoricalMap layer to maps #454

Merged
merged 15 commits into from
Jul 5, 2024
Merged
3 changes: 2 additions & 1 deletion lang/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -170,5 +170,6 @@
"simple": "simple",
"Are you sure?": "Are you sure?",
"This action cannot be undone.": "This action cannot be undone.",
"Yes": "Yes"
"Yes": "Yes",
"Toggle time filter for places": "Toggle time filter for places"
}
10 changes: 8 additions & 2 deletions src/components/GrampsjsMap.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ const defaultConfig = {
leafletZoomOffset: 0,
glStyle: 'https://www.openhistoricalmap.org/map-styles/main/main.json',
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OHM offers multiple official stylesheets. For now, I’ve gone with the default “Historical” style. If desired, Gramps can define its own custom style, following the MapLibre GL Style Specification. Tools like Fresco and Maputnik allow you to design custom styles visually.

glAttribution:
'<a href="https://www.openhistoricalmap.org/copyright">OpenHistoricalMap</a>',
'<a href="https://www.openhistoricalmap.org/">OpenHistoricalMap</a>',
}

class GrampsjsMap extends LitElement {
Expand Down Expand Up @@ -162,7 +162,13 @@ class GrampsjsMap extends LitElement {
filterByDecimalYear(this._gl.getMaplibreMap(), this.year)
return
}
if (this._map !== undefined) {
if (
this._map !== undefined &&
(changed.has('latitude') ||
changed.has('longitude') ||
changed.has('mapid') ||
changed.has('zoom'))
) {
if (this.latMin === 0 && this.latMax === 0) {
this._map.setZoom(this.zoom)
// eslint-disable-next-line new-cap
Expand Down
14 changes: 12 additions & 2 deletions src/components/GrampsjsMapTimeSlider.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {sharedStyles} from '../SharedStyles.js'
import {GrampsjsTranslateMixin} from '../mixins/GrampsjsTranslateMixin.js'
import {fireEvent} from '../util.js'
import {renderIconSvg} from '../icons.js'
import './GrampsjsTooltip.js'

class GrampsjsMapTimeSlider extends GrampsjsTranslateMixin(LitElement) {
static get styles() {
Expand Down Expand Up @@ -80,12 +81,14 @@ class GrampsjsMapTimeSlider extends GrampsjsTranslateMixin(LitElement) {
return {
value: {type: Number},
span: {type: Number},
min: {type: Number},
filterMap: {type: Boolean},
}
}

constructor() {
super()
this.min = 1500
this.value = new Date().getFullYear() - 50
this.span = 50
this.filterMap = false
Expand All @@ -98,7 +101,7 @@ class GrampsjsMapTimeSlider extends GrampsjsTranslateMixin(LitElement) {
@input="${this._handleInput}"
?disabled="${!this.filterMap && this.span < 0}"
labeled
min="1500"
min="${this.min}"
max="${new Date().getFullYear()}"
value="${this.value}"
></md-slider>
Expand All @@ -116,23 +119,30 @@ class GrampsjsMapTimeSlider extends GrampsjsTranslateMixin(LitElement) {
@click="${this._handleSpanClick}"
?disabled="${this.span < 0}"
>
<grampsjs-tooltip for="span-button" .strings="${this.strings}"
>${this._('Span')}</grampsjs-tooltip
>
<md-icon
>${renderIconSvg(mdiCog, 'var(--md-sys-color-primary)')}</md-icon
>
</md-icon-button>
</div>
<md-switch
id="filter-switch"
@input="${this._handleSwitch}"
?selected="${this.span > 0}"
></md-switch>
<grampsjs-tooltip for="filter-switch" .strings="${this.strings}"
>${this._('Toggle time filter for places')}</grampsjs-tooltip
>
</div>
<md-menu
positioning="fixed"
id="span-menu"
anchor="span-button"
skip-restore-focus
>
${[0, 10, 25, 50, 100].map(
${[1, 10, 25, 50, 100].map(
years => html`
<md-menu-item @click="${() => this._handleSpanYearsClick(years)}">
<div slot="headline">&pm;&nbsp;${years}</div>
Expand Down
38 changes: 36 additions & 2 deletions src/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -706,6 +706,38 @@ export function filterByDate(map, dateP) {
filterByDecimalYear(map, decimalYear)
}

export function getGregorianYears(date) {
if (date === undefined || dateIsEmpty(date)) {
return [undefined, undefined]
}

let year1 = date.dateval[2]
let year2 = date.dateval[6] ?? year1

// handle different calendars.
// we appoximate to +/- 1 year, so we ignore the difference
// between Gregorian, Julian, and Swedish, and use simple
// linear approximations to the Islamic, Persian, and Hebrew calendars.
const CAL_HEBREW = 2
const CAL_FRENCH = 3
const CAL_PERSIAN = 4
const CAL_ISLAMIC = 5
if (date.calendar === CAL_HEBREW) {
year1 -= 3760
year2 -= 3760
} else if (date.calendar === CAL_FRENCH) {
year1 += 1791
year2 += 1791
} else if (date.calendar === CAL_PERSIAN) {
year1 += 621
year2 += 621
} else if (date.calendar === CAL_ISLAMIC) {
year1 = Math.floor(0.97022 * year1 + 621.565)
year2 = Math.floor(0.97022 * year2 + 621.565)
}
return [year1, year2]
}

export function isDateBetweenYears(date, yearMin, yearMax) {
const MOD_BEFORE = 1
const MOD_AFTER = 2
Expand All @@ -722,8 +754,10 @@ export function isDateBetweenYears(date, yearMin, yearMax) {
return false
}

let year1 = date.dateval[2]
let year2 = date.dateval[6] ?? year1
let [year1, year2] = getGregorianYears(date)
if (year1 === undefined) {
return false
}

if (date.modifier === MOD_BEFORE || date.modifier === MOD_TO) {
year1 -= RANGE_BEFORE
Expand Down
18 changes: 17 additions & 1 deletion src/views/GrampsjsViewMap.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import '../components/GrampsjsMapSearchbox.js'
import '../components/GrampsjsMapTimeSlider.js'
import '../components/GrampsjsPlaceBox.js'
import {apiGet, getMediaUrl} from '../api.js'
import {isDateBetweenYears} from '../util.js'
import {isDateBetweenYears, getGregorianYears} from '../util.js'
import '@material/mwc-textfield'

// This is used for initial map center in absence of places
Expand Down Expand Up @@ -82,6 +82,7 @@ export class GrampsjsViewMap extends GrampsjsView {
_year: {type: Number},
_yearSpan: {type: Number},
_currentLayer: {type: String},
_minYear: {type: Number},
}
}

Expand All @@ -100,6 +101,7 @@ export class GrampsjsViewMap extends GrampsjsView {
this._year = -1
this._yearSpan = -1
this._currentLayer = ''
this._minYear = 1500
}

renderContent() {
Expand Down Expand Up @@ -132,6 +134,7 @@ export class GrampsjsViewMap extends GrampsjsView {
>${this._renderPlaceDetails()}</grampsjs-map-searchbox
>
<grampsjs-map-time-slider
min="${this._minYear}"
?filterMap="${this._currentLayer === 'OpenHistoricalMap'}"
@timeslider:change="${this._handleTimeSliderChange}"
.strings="${this.strings}"
Expand Down Expand Up @@ -389,12 +392,25 @@ export class GrampsjsViewMap extends GrampsjsView {
if ('data' in data) {
this.error = false
this._dataEvents = data.data.filter(event => event.place)
this._minYear = this._getMinYear()
} else if ('error' in data) {
this.error = true
this._errorMessage = data.error
}
}

_getMinYear() {
const years = this._dataEvents
?.filter(event => event.place)
?.map(event => getGregorianYears(event.date)?.[0])
?.filter(y => y !== undefined)
let minYear = Math.min(...years)
const lastYear = new Date().getFullYear() - 1
minYear = Math.min(minYear, lastYear)
minYear = Math.max(minYear, 1) // disallow negative
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By the way, the map data and filtering code support BCE dates. Quite unlikely, of course, but I mention this in case we do decide to allow it in the future.

The only gotcha is that the year is offset by one because there’s no year 0, per ISO 8601. Currently this component is using −1 as a magic number for an unset year; we’d need to use something else like undefined.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes you're right. I think it's pretty ok for Gramps Web, but of course not ok for OHM in general. So would be more elegant to handle in full generality in the future here as well.

return minYear
}

async _fetchDataLayers() {
const rules = {
rules: [
Expand Down
Loading