From e077f3da022cdb72de4ece2facd0ca7b96b95739 Mon Sep 17 00:00:00 2001 From: Jones Magloire Date: Tue, 25 May 2021 19:20:16 +0200 Subject: [PATCH 1/2] feat: change base map from OSM to Jawg (#225) --- src/views/vmd-lieux.view.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/views/vmd-lieux.view.ts b/src/views/vmd-lieux.view.ts index d104e6c0d..d15cb3db6 100644 --- a/src/views/vmd-lieux.view.ts +++ b/src/views/vmd-lieux.view.ts @@ -82,9 +82,9 @@ export class VmdLieuxView extends LitElement { console.log("error1") }); - tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { + tileLayer('https://{s}.tile.jawg.io/jawg-sunny/{z}/{x}/{y}.png?access-token=sOXVrxPultoFMoo0oQigvvfXgPxaX0OFlFJF7y1rw0ZQy1c1yFTSnXSVOBqw0W6Y', { maxZoom: 19, - attribution: '© OpenStreetMap contributors' + attribution: '© JawgMaps | © OSM contributors ' }).addTo(mymap); } From 4d590125a40e08c5f06daccf89f2677d150fa201 Mon Sep 17 00:00:00 2001 From: Joxit Date: Mon, 14 Jun 2021 12:04:18 +0200 Subject: [PATCH 2/2] feat: add map button on search results --- .../vmd-appointment-map.component.ts | 195 ++++++++++++++++++ src/index.ts | 1 + src/views/vmd-rdv.view.ts | 1 + 3 files changed, 197 insertions(+) create mode 100644 src/components/vmd-appointment-map.component.ts diff --git a/src/components/vmd-appointment-map.component.ts b/src/components/vmd-appointment-map.component.ts new file mode 100644 index 000000000..839331b83 --- /dev/null +++ b/src/components/vmd-appointment-map.component.ts @@ -0,0 +1,195 @@ +import { css, unsafeCSS, customElement, html, LitElement, property, internalProperty } from 'lit-element'; +import { Icon, map, marker, tileLayer, LatLngTuple } from 'leaflet'; +import leafletCss from 'leaflet/dist/leaflet.css'; +import leafletMarkerCss from 'leaflet.markercluster/dist/MarkerCluster.Default.css'; +import * as L from 'leaflet'; +import 'leaflet.markercluster'; +import { Router } from '../routing/Router'; +import { LieuAffichableAvecDistance, Coordinates, SearchRequest, TYPES_LIEUX } from '../state/State'; +import { CSS_Global } from '../styles/ConstructibleStyleSheets'; +import { format as formatDate, parseISO } from 'date-fns'; +import { fr } from 'date-fns/locale'; +import { Strings } from '../utils/Strings'; + +@customElement('vmd-appointment-map') +export class VmdAppointmentMapComponent extends LitElement { + //language=css + static styles = [ + CSS_Global, + css` + ${unsafeCSS(leafletCss)} + `, + css` + ${unsafeCSS(leafletMarkerCss)} + `, + css` + :host { + display: flex; + align-items: center; + flex-direction: column; + } + :host button { + margin: 1em; + } + :host .dialog { + width: 100%; + padding: 0 1em; + } + #appointment-map { + min-height: 500px; + } + `, + ]; + @property({ type: Object, attribute: false }) lieux!: LieuAffichableAvecDistance[]; + @property() private map: L.Map | void = undefined + @property() private showMap: boolean = false + @internalProperty() protected currentSearch: SearchRequest | void = undefined; + + constructor() { + super(); + } + + render() { + console.log(this.currentSearch, this.lieux) + return html` + + ${this.showMap ? html`
`: html``} + `; + } + + private toggleMap() { + this.showMap = !this.showMap; + if (this.map) { + this.map.off(); + this.map.remove(); + this.map = undefined; + } else { + setTimeout(() => { + this.map = this.loadMap(); + }, 10) + // this.requestUpdate() + } + + } + + private loadMap() : L.Map { + const coordinates = + this.currentSearch instanceof SearchRequest.ByCommune && + this.toCoordinates((this.currentSearch as SearchRequest.ByCommune).commune); + const mymap = map(this.shadowRoot!.querySelector('#appointment-map') as HTMLElement).setView( + coordinates || [46.505, 3], + 13 + ); + + tileLayer( + 'https://{s}.tile.jawg.io/jawg-sunny/{z}/{x}/{y}.png?access-token=sOXVrxPultoFMoo0oQigvvfXgPxaX0OFlFJF7y1rw0ZQy1c1yFTSnXSVOBqw0W6Y', + { + maxZoom: 19, + attribution: + '© JawgMaps | © OSM contributors', + } + ).addTo(mymap); + const { markers, bounds } = this.creer_pins(this.lieux, coordinates || [0, 0]); + markers.addTo(mymap); + mymap.fitBounds([ + [bounds.minLat, bounds.minLon], + [bounds.maxLat, bounds.maxLon], + ]); + return mymap + } + + private toCoordinates(o: Coordinates): LatLngTuple | void { + if (o && typeof o.latitude === 'number' && typeof o.longitude === 'number') { + return [o.latitude, o.longitude]; + } + } + + // connectedCallback() { + // super.connectedCallback(); + + // this.requestUpdate().then(() => this.loadMap()); + // } + + private creer_pins(lieux: LieuAffichableAvecDistance[], defaultCoordinates: LatLngTuple) { + const bounds = { + minLat: defaultCoordinates[0] || 180, + maxLat: defaultCoordinates[0] || -180, + minLon: defaultCoordinates[1] || 180, + maxLon: defaultCoordinates[1] || -180, + }; + const markers = lieux + .filter((lieu) => lieu.disponible) + .reduce((markers: L.MarkerClusterGroup, lieu: LieuAffichableAvecDistance) => { + const coordinates = this.toCoordinates(lieu.location); + if (coordinates) { + var string_popup = ` + ${lieu.nom} + + ${lieu.metadata.address} + + ${ + lieu.metadata.phone_number + ? ` + + + + ${Strings.toNormalizedPhoneNumber(lieu.metadata.phone_number)} + + + + ` + : '' + } + + ${TYPES_LIEUX[lieu.type]} + + + ${lieu.vaccine_type} + + + ${this.prochainRDV(lieu)} + + + Prendre rendez-vous + + `; + if (lieu.distance === undefined || lieu.distance < 50) { + bounds.minLat = Math.min(coordinates[0], bounds.minLat); + bounds.maxLat = Math.max(coordinates[0], bounds.maxLat); + bounds.minLon = Math.min(coordinates[1], bounds.minLon); + bounds.maxLon = Math.max(coordinates[1], bounds.maxLon); + } + var newMarker = marker(coordinates, { + icon: new Icon.Default({ imagePath: `${Router.basePath}assets/images/png/` }), + }).bindPopup(string_popup); + newMarker.on('click', function () { + // @ts-ignore + this.openPopup(); + }); + markers.addLayer(newMarker); + } + + return markers; + }, new L.MarkerClusterGroup({ disableClusteringAtZoom: 9 })); + + return { markers, bounds }; + } + private prochainRDV(lieu: LieuAffichableAvecDistance): string { + if (lieu && lieu.prochain_rdv) { + return this.toTitleCase(formatDate(parseISO(lieu.prochain_rdv), "EEEE d MMMM 'à' HH:mm", { locale: fr })); + } else { + return 'Aucun rendez-vous'; + } + } + + private toTitleCase(date: string): string { + return date.replace(/(^|\s)([a-z])(\w)/g, (_, leader, letter, loser) => + [leader, letter.toUpperCase(), loser].join('') + ); + } +} diff --git a/src/index.ts b/src/index.ts index 1a53312b3..57a592584 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,6 +1,7 @@ import './vmd-app.component' import './components/vmd-search.component' import './components/vmd-appointment-card.component' +import './components/vmd-appointment-map.component' import './components/vmd-appointment-metadata.component' import './components/vmd-commune-or-departement-selector.component' import './components/vmd-button-switch.component' diff --git a/src/views/vmd-rdv.view.ts b/src/views/vmd-rdv.view.ts index d8547152c..60da4f7b0 100644 --- a/src/views/vmd-rdv.view.ts +++ b/src/views/vmd-rdv.view.ts @@ -113,6 +113,7 @@ export abstract class AbstractVmdRdvView extends LitElement { />
${this.renderAdditionnalSearchCriteria()} +