Skip to content

Commit

Permalink
feat: station list and map
Browse files Browse the repository at this point in the history
  • Loading branch information
chrismclarke committed Dec 21, 2023
1 parent e4d30d1 commit 6ca1213
Show file tree
Hide file tree
Showing 8 changed files with 124 additions and 56 deletions.
2 changes: 1 addition & 1 deletion apps/picsa-apps/dashboard/project.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
"tsConfig": "apps/picsa-apps/dashboard/tsconfig.app.json",
"inlineStyleLanguage": "scss",
"assets": ["apps/picsa-apps/dashboard/src/favicon.ico", "apps/picsa-apps/dashboard/src/assets"],
"styles": ["apps/picsa-apps/dashboard/src/styles.scss"],
"styles": ["apps/picsa-apps/dashboard/src/styles.scss", "node_modules/leaflet/dist/leaflet.css"],
"stylePreprocessorOptions": {
"includePaths": ["libs/theme/src"]
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,26 @@ <h2 style="flex: 1">Climate Data</h2>
}
</div>
<h3>Stations</h3>
<table mat-table class="stations-table" [dataSource]="service.stations">
<ng-container matColumnDef="station_id">
<th mat-header-cell *matHeaderCellDef>station_id</th>
<td mat-cell *matCellDef="let station">{{ station.station_id }}</td>
</ng-container>
<ng-container matColumnDef="station_name">
<th mat-header-cell *matHeaderCellDef>station_name</th>
<td mat-cell *matCellDef="let station">{{ station.station_name }}</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row class="station-row" [routerLink]="row.id" *matRowDef="let row; columns: displayedColumns"></tr>
</table>
<div style="display: flex; gap: 1rem">
<div style="flex: 1">
<table mat-table class="stations-table" [dataSource]="service.stations" style="width: 200px">
<ng-container matColumnDef="station_id">
<th mat-header-cell *matHeaderCellDef>station_id</th>
<td mat-cell *matCellDef="let station">{{ station.station_id }}</td>
</ng-container>
<ng-container matColumnDef="station_name">
<th mat-header-cell *matHeaderCellDef>station_name</th>
<td mat-cell *matCellDef="let station">{{ station.station_name }}</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr
mat-row
class="station-row"
[routerLink]="row.station_id"
*matRowDef="let row; columns: displayedColumns"
></tr>
</table>
</div>
<picsa-map style="height: 500px; width: 500px" [markers]="mapMarkers"></picsa-map>
</div>
</div>
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,29 @@ import { CommonModule } from '@angular/common';
import { Component, OnInit } from '@angular/core';
import { MatTableModule } from '@angular/material/table';
import { RouterModule } from '@angular/router';
import { IMapMarker, PicsaMapComponent } from '@picsa/shared/features/map/map';

import { ClimateDataDashboardService, IStationRow } from '../../climate-data.service';

@Component({
selector: 'dashboard-climate-data-home',
standalone: true,
imports: [CommonModule, MatTableModule, RouterModule],
imports: [CommonModule, MatTableModule, RouterModule, PicsaMapComponent],
templateUrl: './climate-data-home.component.html',
styleUrls: ['./climate-data-home.component.scss'],
})
export class ClimateDataHomeComponent implements OnInit {
public displayedColumns: (keyof IStationRow)[] = ['station_id', 'station_name'];

public mapMarkers: IMapMarker[];

constructor(public service: ClimateDataDashboardService) {}

async ngOnInit() {
await this.service.ready();
this.mapMarkers = this.service.stations.map((m) => ({
latlng: [m.latitude as number, m.longitude as number],
number: m.station_id,
}));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { CommonModule } from '@angular/common';
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { PicsaNotificationService } from '@picsa/shared/services/core/notification.service';

import { ClimateDataDashboardService, IStationRow } from '../../climate-data.service';

@Component({
selector: 'dashboard-station-page',
standalone: true,
imports: [CommonModule],
templateUrl: './station-page.component.html',
styleUrls: ['./station-page.component.scss'],
})
export class StationPageComponent implements OnInit {
public station: IStationRow | undefined;

public get stationSummary() {
return {
keys: Object.keys(this.station || {}),
values: Object.values(this.station || {}),
};
}

constructor(
private service: ClimateDataDashboardService,
private route: ActivatedRoute,
private notificationService: PicsaNotificationService
) {}

async ngOnInit() {
await this.service.ready();
const { stationId } = this.route.snapshot.params;
this.station = this.service.stations.find((station) => station.station_id === parseInt(stationId));
if (!this.station) {
this.notificationService.showUserNotification({ matIcon: 'error', message: `Station data not found` });
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,6 @@ export class SiteSelectPage implements OnInit {
}

populateSites() {
const iconUrl = STATION_ICON_WHITE;
let stations = HARDCODED_STATIONS;
const { climateTool, localisation } = this.configurationService.activeConfiguration;
const filterFn = climateTool?.stationFilter;
Expand All @@ -65,21 +64,14 @@ export class SiteSelectPage implements OnInit {
} else {
stations = stations.filter((station) => station.countryCode === localisation.country.code);
}
const markers: IMapMarker[] = stations.map((s) => {
const markers: IMapMarker[] = stations.map((s, i) => {
return {
iconUrl,
latlng: [s.latitude, s.longitude],
data: s,
numbered: true,
number: i + 1,
};
});
this.mapMarkers = markers;
return { stations, markers };
}
}

// svg icon hardcoded to data uri
const STATION_ICON_BLACK =
"data:image/svg+xml,%3Csvg width='100px' height='100px' enable-background='new 6.191 0 87.619 100' fill='%23000000' version='1.1' viewBox='6.191 0 87.619 100' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='m82.447 57.766c-3.112 0-5.937 1.255-7.992 3.29l-19.102-13.687v-1.773c0-2.015-1.303-3.692-3.094-4.333v-18.671c5.521-0.768 9.769-5.509 9.769-11.236-1e-3 -6.266-5.082-11.356-11.361-11.356-6.266 0-11.348 5.091-11.348 11.355 0 5.727 4.247 10.468 9.77 11.236v18.688c-1.76 0.662-3.027 2.335-3.027 4.316v1.604l-20.156 13.865c-2.071-2.262-5.049-3.678-8.355-3.678-6.277 0-11.36 5.087-11.36 11.357s5.083 11.356 11.36 11.356c6.265 0 11.348-5.087 11.348-11.356 0-1.818-0.436-3.544-1.193-5.066l18.353-12.622v44.299c0 2.561 2.089 4.646 4.647 4.646 2.564 0 4.646-2.085 4.646-4.646v-44.084l17.183 12.314c-0.91 1.632-1.437 3.524-1.437 5.529 0 6.283 5.087 11.357 11.351 11.357 6.273 0 11.36-5.074 11.36-11.357-1e-3 -6.269-5.088-11.347-11.362-11.347z'/%3E%3C/svg%3E%0A";
const STATION_ICON_WHITE =
"data:image/svg+xml,%3Csvg width='100px' height='100px' enable-background='new 6.191 0 87.619 100' fill='%23000000' version='1.1' viewBox='6.191 0 87.619 100' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='m82.447 57.766c-3.112 0-5.937 1.255-7.992 3.29l-19.102-13.687v-1.773c0-2.015-1.303-3.692-3.094-4.333v-18.671c5.521-0.768 9.769-5.509 9.769-11.236-1e-3 -6.266-5.082-11.356-11.361-11.356-6.266 0-11.348 5.091-11.348 11.355 0 5.727 4.247 10.468 9.77 11.236v18.688c-1.76 0.662-3.027 2.335-3.027 4.316v1.604l-20.156 13.865c-2.071-2.262-5.049-3.678-8.355-3.678-6.277 0-11.36 5.087-11.36 11.357s5.083 11.356 11.36 11.356c6.265 0 11.348-5.087 11.348-11.356 0-1.818-0.436-3.544-1.193-5.066l18.353-12.622v44.299c0 2.561 2.089 4.646 4.647 4.646 2.564 0 4.646-2.085 4.646-4.646v-44.084l17.183 12.314c-0.91 1.632-1.437 3.524-1.437 5.529 0 6.283 5.087 11.357 11.351 11.357 6.273 0 11.36-5.074 11.36-11.357-1e-3 -6.269-5.088-11.347-11.362-11.347z' fill='%23fff'/%3E%3C/svg%3E%0A";
49 changes: 34 additions & 15 deletions libs/shared/src/features/map/map.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,17 @@ export class PicsaMapComponent {

@Input() mapOptions: L.MapOptions = {};
@Input() basemapOptions: Partial<IBasemapOptions> = {};
@Input() markers: IMapMarker[];

private _markers: IMapMarker[];
@Input() set markers(markers: IMapMarker[]) {
if (markers) {
this._markers = markers;
// add markers if map already initialised, otherwise will be added onMapReady
if (this.map) {
this.addMarkers(markers);
}
}
}

// make native map element available directly
public map: L.Map;
Expand All @@ -31,6 +41,7 @@ export class PicsaMapComponent {
private _activeMarker: L.Marker;
// default options are overwritten via input setter
_mapOptions: L.MapOptions = MAP_DEFAULTS;

ngOnInit() {
// the user provides basemap options separate to general map options, so combine here
// define the basemap layer and then bind to the view component
Expand All @@ -40,43 +51,44 @@ export class PicsaMapComponent {
this._mapOptions = { ...mapOptions, layers: [basemap] };
}

private addMarkers(mapMarkers: IMapMarker[], popupContent?: HTMLDivElement) {
private addMarkers(mapMarkers: IMapMarker[], fitMap = true) {
mapMarkers.forEach((m, i) => {
const icon = L.icon({
...ICON_DEFAULTS,
iconUrl: m.iconUrl,
iconUrl: m.iconUrl || STATION_ICON_WHITE,
});
const activeIcon = L.icon({
...ACTIVE_ICON_DEFAULTS,
iconUrl: m.iconUrl,
iconUrl: m.iconUrl || STATION_ICON_WHITE,
});
const marker = L.marker(m.latlng, { icon });
if (m.numbered) {
if (m.number) {
const toolTip = L.tooltip(NUMBER_TOOLTIP_DEFAULTS);
marker
.bindTooltip(toolTip)
.setTooltipContent(`${i + 1}`)
.openTooltip();
marker.bindTooltip(toolTip).setTooltipContent(`${m.number}`).openTooltip();
}
marker.on({
click: () => this._onMarkerClick(m, marker, activeIcon, icon),
});
marker.addTo(this.map);
});
if (fitMap) {
this.fitMapToMarkers(mapMarkers);
}
}

// when the map is ready it emits event with map, and also binds map to
// public api to be accessed by other services
_onMapReady(map: L.Map) {
this.map = map;
this.addMarkers(this.markers);
this.fitMapToMarkers();
if (this._markers) {
this.addMarkers(this._markers);
}
this.onMapReady.emit(map);
}

/** Calculate a bounding rectangle that covers all points and fit within map */
private fitMapToMarkers() {
const latLngs = this.markers.map((m) => m.latlng);
private fitMapToMarkers(markers: IMapMarker[]) {
const latLngs = markers.map((m) => m.latlng);
const bounds = new L.LatLngBounds(latLngs as any);
this.map.fitBounds(bounds, { maxZoom: 8, padding: [10, 10] });
}
Expand Down Expand Up @@ -175,12 +187,19 @@ const NUMBER_TOOLTIP_DEFAULTS: L.TooltipOptions = {

type IFeaturedCountry = 'malawi' | 'kenya';
export interface IMapMarker {
iconUrl: string;
iconUrl?: string;
latlng: L.LatLngTuple;
numbered?: boolean;
/** Display number with icon */
number?: number;
data?: any;
}
export interface IBasemapOptions extends L.TileLayerOptions {
src: string;
}
export type IMapOptions = L.MapOptions;

// svg icon hardcoded to data uri
const STATION_ICON_BLACK =
"data:image/svg+xml,%3Csvg width='100px' height='100px' enable-background='new 6.191 0 87.619 100' fill='%23000000' version='1.1' viewBox='6.191 0 87.619 100' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='m82.447 57.766c-3.112 0-5.937 1.255-7.992 3.29l-19.102-13.687v-1.773c0-2.015-1.303-3.692-3.094-4.333v-18.671c5.521-0.768 9.769-5.509 9.769-11.236-1e-3 -6.266-5.082-11.356-11.361-11.356-6.266 0-11.348 5.091-11.348 11.355 0 5.727 4.247 10.468 9.77 11.236v18.688c-1.76 0.662-3.027 2.335-3.027 4.316v1.604l-20.156 13.865c-2.071-2.262-5.049-3.678-8.355-3.678-6.277 0-11.36 5.087-11.36 11.357s5.083 11.356 11.36 11.356c6.265 0 11.348-5.087 11.348-11.356 0-1.818-0.436-3.544-1.193-5.066l18.353-12.622v44.299c0 2.561 2.089 4.646 4.647 4.646 2.564 0 4.646-2.085 4.646-4.646v-44.084l17.183 12.314c-0.91 1.632-1.437 3.524-1.437 5.529 0 6.283 5.087 11.357 11.351 11.357 6.273 0 11.36-5.074 11.36-11.357-1e-3 -6.269-5.088-11.347-11.362-11.347z'/%3E%3C/svg%3E%0A";
const STATION_ICON_WHITE =
"data:image/svg+xml,%3Csvg width='100px' height='100px' enable-background='new 6.191 0 87.619 100' fill='%23000000' version='1.1' viewBox='6.191 0 87.619 100' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='m82.447 57.766c-3.112 0-5.937 1.255-7.992 3.29l-19.102-13.687v-1.773c0-2.015-1.303-3.692-3.094-4.333v-18.671c5.521-0.768 9.769-5.509 9.769-11.236-1e-3 -6.266-5.082-11.356-11.361-11.356-6.266 0-11.348 5.091-11.348 11.355 0 5.727 4.247 10.468 9.77 11.236v18.688c-1.76 0.662-3.027 2.335-3.027 4.316v1.604l-20.156 13.865c-2.071-2.262-5.049-3.678-8.355-3.678-6.277 0-11.36 5.087-11.36 11.357s5.083 11.356 11.36 11.356c6.265 0 11.348-5.087 11.348-11.356 0-1.818-0.436-3.544-1.193-5.066l18.353-12.622v44.299c0 2.561 2.089 4.646 4.647 4.646 2.564 0 4.646-2.085 4.646-4.646v-44.084l17.183 12.314c-0.91 1.632-1.437 3.524-1.437 5.529 0 6.283 5.087 11.357 11.351 11.357 6.273 0 11.36-5.074 11.36-11.357-1e-3 -6.269-5.088-11.347-11.362-11.347z' fill='%23fff'/%3E%3C/svg%3E%0A";
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
"@angular/platform-browser-dynamic": "17.0.3",
"@angular/router": "17.0.3",
"@angular/service-worker": "17.0.3",
"@asymmetrik/ngx-leaflet": "^14.0.1",
"@asymmetrik/ngx-leaflet": "^17.0.0",
"@awesome-cordova-plugins/core": "^6.4.0",
"@awesome-cordova-plugins/file": "^6.4.0",
"@awesome-cordova-plugins/file-opener": "^6.4.0",
Expand Down Expand Up @@ -92,7 +92,7 @@
"hls.js": "^1.4.12",
"html2canvas": "^1.4.1",
"intro.js": "^7.0.1",
"leaflet": "^1.9.3",
"leaflet": "^1.9.4",
"lottie-web": "^5.10.2",
"mobx": "^6.7.0",
"mobx-angular": "^4.7.1",
Expand Down Expand Up @@ -140,7 +140,7 @@
"@types/hammerjs": "^2.0.41",
"@types/intro.js": "^5.1.1",
"@types/jest": "29.4.4",
"@types/leaflet": "^1.8.0",
"@types/leaflet": "^1.9.4",
"@types/node": "^18.14.2",
"@types/papaparse": "^5.3.2",
"@types/parse": "^2.18.16",
Expand Down
28 changes: 14 additions & 14 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -594,16 +594,16 @@ __metadata:
languageName: node
linkType: hard

"@asymmetrik/ngx-leaflet@npm:^14.0.1":
version: 14.0.1
resolution: "@asymmetrik/ngx-leaflet@npm:14.0.1"
"@asymmetrik/ngx-leaflet@npm:^17.0.0":
version: 17.0.0
resolution: "@asymmetrik/ngx-leaflet@npm:17.0.0"
dependencies:
tslib: ^2.3.0
peerDependencies:
"@angular/common": 14
"@angular/core": 14
"@angular/common": 17
"@angular/core": 17
leaflet: 1
checksum: c30fbfd3821cea0e8c05b3da914cee1580eb0ce2670e53f234cd15691115127b28c180665850b856da0c4c2ea159de257daf5571171bf49033e2576c33a6df58
checksum: 504be40bbb9d9425e133824836f84a3300bb0220f530bd7abba4a9cb7c1122db6bee4fec8fd61ffe42d40c5efa6936ab07fde13006d48b91b3606c5bfeb668bc
languageName: node
linkType: hard

Expand Down Expand Up @@ -8357,12 +8357,12 @@ __metadata:
languageName: node
linkType: hard

"@types/leaflet@npm:^1.8.0":
version: 1.9.4
resolution: "@types/leaflet@npm:1.9.4"
"@types/leaflet@npm:^1.9.4":
version: 1.9.8
resolution: "@types/leaflet@npm:1.9.8"
dependencies:
"@types/geojson": "*"
checksum: ae5b4422b6eb912e6c6910d049f63cb96537eac46af4a897471ab98c642d059a32789f411157b873a5dafdbc9cef3399dc0673c5f64be067eebc530c43783e75
checksum: d6a33d5db0bdf608957cd52701b17ee89e936034a8f0ab619b8e7ef70312a0da177837dd125756e83af036276a8b299cc724c9ffb3caa7d22893fea2791e1e13
languageName: node
linkType: hard

Expand Down Expand Up @@ -16467,7 +16467,7 @@ __metadata:
languageName: node
linkType: hard

"leaflet@npm:^1.9.3":
"leaflet@npm:^1.9.3, leaflet@npm:^1.9.4":
version: 1.9.4
resolution: "leaflet@npm:1.9.4"
checksum: bfc79f17a247b37b92d84b3c78702501603392d6589fde606de4a825d11f1609d90225388834f2e0709dac327e52dcd4b4b9cc9fd3d590060c5b1e53b84fa6c6
Expand Down Expand Up @@ -18937,7 +18937,7 @@ __metadata:
"@angular/platform-browser-dynamic": 17.0.3
"@angular/router": 17.0.3
"@angular/service-worker": 17.0.3
"@asymmetrik/ngx-leaflet": ^14.0.1
"@asymmetrik/ngx-leaflet": ^17.0.0
"@awesome-cordova-plugins/core": ^6.4.0
"@awesome-cordova-plugins/file": ^6.4.0
"@awesome-cordova-plugins/file-opener": ^6.4.0
Expand Down Expand Up @@ -18978,7 +18978,7 @@ __metadata:
"@types/hammerjs": ^2.0.41
"@types/intro.js": ^5.1.1
"@types/jest": 29.4.4
"@types/leaflet": ^1.8.0
"@types/leaflet": ^1.9.4
"@types/node": ^18.14.2
"@types/papaparse": ^5.3.2
"@types/parse": ^2.18.16
Expand Down Expand Up @@ -19033,7 +19033,7 @@ __metadata:
jest-preset-angular: 13.1.4
jetifier: ^2.0.0
jsonc-eslint-parser: ^2.4.0
leaflet: ^1.9.3
leaflet: ^1.9.4
lint-staged: ^13.2.1
lottie-web: ^5.10.2
mobx: ^6.7.0
Expand Down

0 comments on commit 6ca1213

Please sign in to comment.