Skip to content
Snippets Groups Projects
Commit fd31d134 authored by Peter Morstein's avatar Peter Morstein
Browse files

add temperature popup chart

parent b5df86ac
No related branches found
No related tags found
No related merge requests found
import Vue from 'vue'; import Vue from 'vue';
import App from './App.vue'; import App from './App.vue';
import vuetify from './src/plugins/vuetify' import vuetify from './src/plugins/vuetify'
import 'regenerator-runtime';
//new Vue({ render: createElement => createElement(App) }).$mount('#app'); //new Vue({ render: createElement => createElement(App) }).$mount('#app');
new Vue({vuetify, render: createElement => createElement(App)}).$mount('#app') new Vue({vuetify, render: createElement => createElement(App)}).$mount('#app')
\ No newline at end of file
This diff is collapsed.
...@@ -7,6 +7,8 @@ ...@@ -7,6 +7,8 @@
"build": "parcel build --public-url . index.html" "build": "parcel build --public-url . index.html"
}, },
"dependencies": { "dependencies": {
"core-js": "^3.18.0",
"d3": "^7.0.3",
"ol": "^6.6.1", "ol": "^6.6.1",
"vue": "^2.6.14", "vue": "^2.6.14",
"vue-class-component": "^7.2.6", "vue-class-component": "^7.2.6",
...@@ -16,11 +18,15 @@ ...@@ -16,11 +18,15 @@
"vuetify": "^2.5.8" "vuetify": "^2.5.8"
}, },
"devDependencies": { "devDependencies": {
"@types/d3": "^7.0.0",
"@vue/component-compiler-utils": "^3.2.2", "@vue/component-compiler-utils": "^3.2.2",
"parcel": "^2.0.0-rc.0", "parcel": "^2.0.0-rc.0",
"parcel-bundler": "^1.12.5", "parcel-bundler": "^1.12.5",
"regenerator-runtime": "^0.13.9",
"typescript": "^4.4.3", "typescript": "^4.4.3",
"vue-template-compiler": "^2.6.14" "vue-template-compiler": "^2.6.14"
}, },
"browserslist": "defaults" "browserslist": [
"last 2 Chrome versions"
]
} }
<template>
<div ref="station-chart" id="stationChartSVG" width="200px" height="200px"></div>
</template>
<script lang="ts">
import * as d3 from 'd3';
import {Vue, Ref } from 'vue-property-decorator';
import {TemperatureSeries} from './StationLayer';
export default class StationChartPopup extends Vue{
name = 'StationChartPopup';
private d3svg: any;
private path: any;
private xScale: any;
private yScale: any;
private yAxis: any;
private yAxisContainer: any;
private xAxisContainer: any;
private xAxis: any;
private margin = {top: 10, right: 30, bottom: 30, left: 60}
private width = 500 - this.margin.left - this.margin.right;
private height = 200 - this.margin.top - this.margin.bottom;
mounted(){
}
public renderGraph(tSeries: TemperatureSeries[]): void{
if (!this.path){
console.log("new path 2")
this.d3svg = d3.select("#stationChartSVG")
.append("svg")
.attr("width", this.width + this.margin.left + this.margin.right)
.attr("height", this.height + this.margin.top + this.margin.bottom)
.append("g")
.attr("transform", "translate("+this.margin.left+","+this.margin.top+")");
this.xScale = d3.scaleLinear()
.domain(d3.extent(tSeries, function(d){return d.date}))
.range([0, this.width]);
this.xAxis = d3.axisBottom(this.xScale).tickFormat(d3.format("d"));
this.xAxisContainer = this.d3svg.append("g")
.attr("transform", "translate(0,"+this.height+")")
.call(this.xAxis);
this.yScale = d3.scaleLinear()
.domain([d3.min(tSeries, function(d){return d.value}), d3.max(tSeries, function(d){return d.value})])
.range([this.height, 0]);
this.yAxis = d3.axisLeft(this.yScale);
this.yAxisContainer = this.d3svg.append("g")
.call(this.yAxis);
}
this.xScale.domain(d3.extent(tSeries, function(d){return d.date})).range([0, this.width]);
this.xAxis = d3.axisBottom(this.xScale).tickFormat(d3.format("d"));
this.xAxisContainer.call(this.xAxis).transition().duration(1000);
this.yScale.domain([d3.min(tSeries, function(d){return d.value}), d3.max(tSeries, function(d){return d.value})])
.range([this.height, 0]).nice();
this.yAxisContainer.call(this.yAxis).transition()
.duration(1000);
this.path = this.d3svg.selectAll(".t-line")
.data([tSeries], function(d){ return d.value });
this.path.enter().append("path")
.attr("class", "t-line")
.merge(this.path)
.transition()
.duration(1000)
.attr("d", d3.line()
.x((d)=>{return this.xScale(d.date)})
.y((d)=>{return this.yScale(d.value)}))
.attr("fill", "none")
.attr("stroke", "blue")
.attr("stroke-width", 2);
}
}
</script>
...@@ -4,8 +4,13 @@ import GeoJSON from 'ol/format/GeoJSON'; ...@@ -4,8 +4,13 @@ import GeoJSON from 'ol/format/GeoJSON';
import {Circle as CircleStyle, Fill, Stroke, Style, Text} from 'ol/style'; import {Circle as CircleStyle, Fill, Stroke, Style, Text} from 'ol/style';
import MapProperties from './MapProperties'; import MapProperties from './MapProperties';
export default class StationLayer{ export interface TemperatureSeries{
date: number;
value: number;
}
export default class StationLayer{
public static readonly layerName: string = "StationLayer";
private layer: any; private layer: any;
private mapProperties: MapProperties; private mapProperties: MapProperties;
...@@ -13,6 +18,7 @@ export default class StationLayer{ ...@@ -13,6 +18,7 @@ export default class StationLayer{
this.mapProperties = mapProperties; this.mapProperties = mapProperties;
this.layer = new VectorLayer({ this.layer = new VectorLayer({
className: StationLayer.layerName,
source: new VectorSource({ source: new VectorSource({
url: "http://localhost/germanStation.geojson", url: "http://localhost/germanStation.geojson",
format: new GeoJSON(), format: new GeoJSON(),
...@@ -27,7 +33,6 @@ export default class StationLayer{ ...@@ -27,7 +33,6 @@ export default class StationLayer{
protected stationStyle(feature){ protected stationStyle(feature){
console.log(feature.getProperties());
let fontSize = 15 let fontSize = 15
let label = '\u272A'; let label = '\u272A';
if(this.mapProperties.getZoomLevel()>=8){ if(this.mapProperties.getZoomLevel()>=8){
...@@ -47,4 +52,22 @@ export default class StationLayer{ ...@@ -47,4 +52,22 @@ export default class StationLayer{
}); });
} }
public getTemperatureSeriesFromFeature(feature: any): TemperatureSeries[]{
let series: TemperatureSeries[] = new Array<TemperatureSeries>();
console.log(feature);
/* series.push({date: 2010, value: feature.get(2010)});
series.push({date: 2011, value: feature.get(2011)});
series.push({date: 2012, value: feature.get(2012)});
series.push({date: 2013, value: feature.get(2013)});
series.push({date: 2014, value: feature.get(2014)});
*/
feature.getKeys().forEach(element => {
if(!isNaN(element) && Number.parseInt(element)>=1800 && feature.get(element)!=null){
series.push({date: Number.parseInt(element), value: feature.get(element)});
}
});
return series;
}
} }
\ No newline at end of file
...@@ -2,29 +2,84 @@ ...@@ -2,29 +2,84 @@
<template> <template>
<div class="weatherContainer"> <div class="weatherContainer">
<div ref="weathermap" class="weathermap" /> <div ref="weathermap" class="weathermap" />
<div ref="popup" class="ol-popup">
<a href="#" ref="popup-closer" class="ol-popup-closer"></a>
<div ref="popup-content">
</div>
<StationChartPopup ref="StationChartPopup"/>
</div>
</div> </div>
</template> </template>
<style scoped> <style scoped>
.weathermap { .weathermap {
width: inherit; width: 100%;
height: inherit; height: 100%;
position:absolute;
} }
.ol-popup {
position: absolute;
background-color: white;
box-shadow: 0 1px 4px rgba(0,0,0,0.2);
padding: 15px;
border-radius: 10px;
border: 1px solid #cccccc;
bottom: 12px;
left: -50px;
min-width: 280px;
}
.ol-popup:after, .ol-popup:before {
top: 100%;
border: solid transparent;
content: " ";
height: 0;
width: 0;
position: absolute;
pointer-events: none;
}
.ol-popup:after {
border-top-color: white;
border-width: 10px;
left: 48px;
margin-left: -10px;
}
.ol-popup:before {
border-top-color: #cccccc;
border-width: 11px;
left: 48px;
margin-left: -11px;
}
.ol-popup-closer {
text-decoration: none;
position: absolute;
top: 2px;
right: 8px;
}
.ol-popup-closer:after {
content: "✖";
}
</style> </style>
<script lang="ts"> <script lang="ts">
import {Vue, Ref, Component } from 'vue-property-decorator'; import {Vue, Ref, Component } from 'vue-property-decorator';
import View from 'ol/View'; import View from 'ol/View';
import Map from 'ol/Map'; import Map from 'ol/Map';
import OSM from 'ol/source/OSM'; import OSM from 'ol/source/OSM';
import Overlay from 'ol/Overlay';
import TileLayer from 'ol/layer/Tile'; import TileLayer from 'ol/layer/Tile';
import { fromLonLat} from 'ol/proj'; import { fromLonLat} from 'ol/proj';
import 'ol/ol.css'; import 'ol/ol.css';
import StationLayer from './StationLayer'; import StationLayer from './StationLayer';
import MapProperties from './MapProperties'; import MapProperties from './MapProperties';
import StationChartPopup from './StationChartPopup.vue';
import { component } from 'vue/types/umd';
@Component({name : 'WeatherMap'})
@Component({components:{StationChartPopup})
export default class WeatherMap extends Vue{ export default class WeatherMap extends Vue{
@Ref('weathermap') readonly targetElement!: HTMLElement; @Ref('weathermap') readonly targetElement!: HTMLElement;
...@@ -32,7 +87,23 @@ export default class WeatherMap extends Vue{ ...@@ -32,7 +87,23 @@ export default class WeatherMap extends Vue{
private projection: string = 'EPSG:3857'; //4236 private projection: string = 'EPSG:3857'; //4236
private mapProperties: MapProperties; private mapProperties: MapProperties;
private stationLayer: StationLayer; private stationLayer: StationLayer;
@Ref('StationChartPopup') stationChartPopup: StationChartPopup;
@Ref('popup') container: HTMLElement;
@Ref('popup-content') content: HTMLElement;
@Ref('popup-closer') closer: HTMLElement;
private overlay:Overlay;
public mounted(): void{ public mounted(): void{
this.overlay = new Overlay({
element: this.container,
autoPan: true,
autoPanAnimation: {
duration: 250,
},
});
this.map = new Map({ this.map = new Map({
target: this.targetElement, target: this.targetElement,
layers: [ layers: [
...@@ -45,13 +116,53 @@ export default class WeatherMap extends Vue{ ...@@ -45,13 +116,53 @@ export default class WeatherMap extends Vue{
center: fromLonLat([11, 51.3]), center: fromLonLat([11, 51.3]),
constrainResolution: true, constrainResolution: true,
projection: this.projection projection: this.projection
}) }),
overlays: [this.overlay],
}); });
this.mapProperties = new MapProperties(this.map, this.projection); this.mapProperties = new MapProperties(this.map, this.projection);
this.appendStationLayer(); this.appendStationLayer();
this.map.on('click', (evt) => {
const coordinate = evt.coordinate;
console.log(coordinate);
var openPopup: boolean = false;
this.map.forEachFeatureAtPixel(evt.pixel, (feature, layer)=> {
if(layer.getClassName()===StationLayer.layerName){
this.stationChartPopup.renderGraph(this.stationLayer.getTemperatureSeriesFromFeature(feature));
openPopup = true;
this.content.innerHTML = '<p>Station: ' + feature.get("id") + '</p>';
}else{
}
/* if(markerList.indexOf(feature)>=0){
console.log(feature);
} */
});
if(openPopup){
this.overlay.setPosition(coordinate);
}else{
this.closePopUp();
}
});
this.closer.onclick = () => {
this.closePopUp();
};
}
private closePopUp():void{
this.overlay.setPosition(undefined);
this.closer.blur();
} }
private appendStationLayer(){ private appendStationLayer(){
......
{ {
"compilerOptions": { "compilerOptions": {
"target":"esnext", "target":"es5",
"module":"esnext", "module":"esnext",
"moduleResolution": "node", "moduleResolution": "node",
"strict": true,
"esModuleInterop": true, "esModuleInterop": true,
"jsx": "preserve",
"experimentalDecorators": true, "experimentalDecorators": true,
"emitDecoratorMetadata": true, "emitDecoratorMetadata": true,
"allowSyntheticDefaultImports": true,
"allowJs": true, "allowJs": true,
"noEmit": true, "noEmit": true,
"baseUrl": ".", "baseUrl": ".",
...@@ -17,5 +20,6 @@ ...@@ -17,5 +20,6 @@
"./*" "./*"
] ]
} }
} },
"exclude": ["node_modules"]
} }
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment