Skip to content

Commit

Permalink
Graph of temperature changes for the year in the burg (#733)
Browse files Browse the repository at this point in the history
* Option for exporting grid cell (#731)

* Added Save Option for Api-like json export.
Will be updated and set to a scheme

* Save Option for Api-like added

* Moved UI of json export to export dialog.
Moved json export to another file named export-json.js

* Added Seperated json export selections
- all JSON : exports all json data.
- minimal JSON : exports json data without cells data.
- cells JSON : exports json data only with pack.cells data.

* More Stable Cell Export...

* Grid cells export option.

* Graph of temperature changes for the year in the burg

I'm bad at writing in English. Please excuse me for this.
It seems illogical to me the parameter of the average temperature for the year in the burg.
Therefore, I found a database of meteorological measurements at https://ru.climate-data.org/
And I analyzed it. With the help of a neural network. I approximately established the dependence of Dy[X] and Dd[X], Md[X] = f(t,p,l).
Dy[X] - how much the temperature in winter differs from the temperature in summer
Md[X] - how much the temperature during the day differs from the temperature at night, on average per year
Dd[X] - how much the temperature difference between day and night is not constant in winter and summer
t - average temperature for the year
p - average rainfall for the year
l - latitude
Now, for each city, you can plot the temperature change. Based on t,p and l.

ATTENTION!!!
The characteristics of the planet and its orbit coincide with those of the earth!

Further, I will use the Russian language, if you want to know my reasoning, please use the Google Translate capabilities.

Я изменил свою первоначальную идею и доработал её. Что касается качества и используемых функций - жду критики. JavaScript - не знакомый мне язык и я не уверен, что выполнил всё верно.
Что касается самой идеи - её реализация пока далека от совершенства. Я понял, что если установить в настройках мира температуру на экваторе ниже, чем на Земле, то графики получаются слишком далекими от реальности. Эта ошибка связана с тем, что Dy[X] зависит от широты, а не среднегодовой температуры. К сожалению добиться хорошей сходимости удалось только расширением нейросети - ещё больше магических чисел.
Теперь, на основе прошлой выборки из 899 городов, попущенных через нейросеть миллион раз среднеквадратичная ошибка стала 3%.
Входными данными для сети являются широта и влажность, а средняя температура иcпользуется только как ориентир при построении графиков.

* Add legnd in graph

* Editing Coefficients

The coefficients have been changed so that the sign of the latitude does not affect the result

Co-authored-by: Efruz Yıldırır <30903352+yldrefruz@users.noreply.github.com>
  • Loading branch information
Kerravitarr and yldrefruz authored Feb 3, 2022
1 parent 2608a27 commit ccb7557
Show file tree
Hide file tree
Showing 4 changed files with 259 additions and 7 deletions.
5 changes: 4 additions & 1 deletion index.html
Original file line number Diff line number Diff line change
Expand Up @@ -2085,6 +2085,7 @@
<button id="burglLegend" data-tip="Edit free text notes (legend) for this burg" class="icon-edit"></button>
<button id="burgLock" class="icon-lock-open" onmouseover="showElementLockTip(event)"></button>
<button id="burgRemove" data-tip="Remove non-capital burg. Shortcut: Delete" class="icon-trash fastDelete"></button>
<button id="burgTemperatureGraph" data-tip="Shows the temperature graph in the burg" class="icon-chart-area"></button>
</div>
</div>

Expand Down Expand Up @@ -3611,7 +3612,8 @@
<div>
<button onclick="exportToJson('Full')" data-tip="Download full data as in JSON format">full</button>
<button onclick="exportToJson('Minimal')" data-tip="Download minimal data as in JSON format">minimal</button>
<button onclick="exportToJson('Cells')" data-tip="Download map metadata and cells data as in JSON format">cells</button>
<button onclick="exportToJson('PackCells')" data-tip="Download map metadata and pack cells data as in JSON format">pack cells</button>
<button onclick="exportToJson('GridCells')" data-tip="Download map metadata and grid cells data as in JSON format">grid cells</button>
</div>
<p>Export in JSON format can be used as an API replacement.</p>

Expand Down Expand Up @@ -4425,6 +4427,7 @@
<script defer src="modules/ui/cultures-editor.js"></script>
<script defer src="modules/ui/namesbase-editor.js"></script>
<script defer src="modules/ui/elevation-profile.js"></script>
<script defer src="modules/ui/temperature-graph.js"></script>
<script defer src="modules/ui/routes-editor.js"></script>
<script defer src="modules/ui/ice-editor.js"></script>
<script defer src="modules/ui/lakes-editor.js"></script>
Expand Down
35 changes: 29 additions & 6 deletions modules/export-json.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ function exportToJson(type) {
const typeMap = {
Full: getFullDataJson,
Minimal: getMinimalDataJson,
Cells: getCellsDataJson
PackCells: getPackCellsDataJson,
GridCells: getGridCellsDataJson,
};

const mapData = typeMap[type]();
Expand Down Expand Up @@ -64,7 +65,7 @@ function getSettings() {
return settings;
}

function getCellsData() {
function getPackCellsData() {
const cellConverted = {
i: Array.from(pack.cells.i),
v: pack.cells.v,
Expand All @@ -91,7 +92,6 @@ function getCellsData() {
religion: Array.from(pack.cells.religion),
province: Array.from(pack.cells.province)
};

const cellObjArr = [];
{
cellConverted.i.forEach(value => {
Expand Down Expand Up @@ -140,12 +140,24 @@ function getCellsData() {
return cellsData;
}

//data only containing graphical appearance
function getGridCellsData(){
const gridData = {
spacing: grid.spacing,
cellsY: grid.cellsY,
cellsX: grid.cellsX,
points: grid.points,
boundary: grid.boundary
}
return gridData
}

function getFullDataJson() {
TIME && console.time("getFullDataJson");

const info = getMapInfo();
const settings = getSettings();
const cells = getCellsData();
const cells = getPackCellsData();
const exportData = {info, settings, coords: mapCoordinates, cells, biomes: biomesData, notes, nameBases};

TIME && console.timeEnd("getFullDataJson");
Expand Down Expand Up @@ -174,13 +186,24 @@ function getMinimalDataJson() {
return JSON.stringify(exportData);
}

function getCellsDataJson() {
function getPackCellsDataJson() {
TIME && console.time("getCellsDataJson");

const info = getMapInfo();
const cells = getCellsData();
const cells = getPackCellsData();
const exportData = {info, cells};

TIME && console.timeEnd("getCellsDataJson");
return JSON.stringify(exportData);
}

function getGridCellsDataJson() {
TIME && console.time("getGridCellsDataJson");

const info = getMapInfo();
const gridCells = getGridCellsData()
const exportData = {info,gridCells};

TIME && console.log("getGridCellsDataJson");
return JSON.stringify(exportData);
}
6 changes: 6 additions & 0 deletions modules/ui/burg-editor.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ function editBurg(id) {
document.getElementById("burglLegend").addEventListener("click", editBurgLegend);
document.getElementById("burgLock").addEventListener("click", toggleBurgLockButton);
document.getElementById("burgRemove").addEventListener("click", removeSelectedBurg);
document.getElementById("burgTemperatureGraph").addEventListener("click", showTemperatureGraphs);

function updateBurgValues() {
const id = +elSelected.attr("data-id");
Expand Down Expand Up @@ -542,6 +543,11 @@ function editBurg(id) {
const name = elSelected.text();
editNotes("burg" + id, name);
}
function showTemperatureGraphs() {
const id = elSelected.attr("data-id");
showTGForBurg(id);
}


function removeSelectedBurg() {
const id = +elSelected.attr("data-id");
Expand Down
220 changes: 220 additions & 0 deletions modules/ui/temperature-graph.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,220 @@
"use strict";

function showTGForBurg(id) {
const b = pack.burgs[id];
const lat = mapCoordinates.latN - (b.y / graphHeight) * mapCoordinates.latT;
const temperature = grid.cells.temp[pack.cells.g[b.cell]];
const prec = grid.cells.prec[pack.cells.g[b.cell]];

const weights = [
[//Layer0
//IN0 IN1
[10.782752257744338, 2.7100404240962126],
[-2.8226802110591462, 51.62920138583541],
[-6.6250956268643835, 4.427939197315455],
[-59.64690518541339, 41.89084162654791],
[-1.3302059550553835, -3.6964487738450913],
[-2.5844898544535497, 0.09879268612455298],
[-5.58528252533573, -0.23426224364501905],
[26.94531337690372, 20.898158905988907],
[3.816397481634785, -0.19045424064580757],
[-4.835697931609101, -10.748232783636434]
],
[//Layer1
[-2.478952081870123, 0.6405800134306895, -7.136785640930911, -0.2186529024764509, 3.6568435212735424, 31.446026153530838, -19.91005187482281, 0.2543395274783306, -7.036924569659988, -0.7721371621651565],
[-197.10583739743538, 6.889921141533474, 0.5058941504631129, 7.7667203434606416, -53.74180550086929, -15.717331715167001, -61.32068414155791, -2.259728220978728, 35.84049189540032, 94.6157364730977],
[-5.312011591880851, -0.09923148954215096, -1.7132477487917586, -22.55559652066422, 0.4806107280554336, -26.5583974109492, 2.0558257347014863, 25.815645234787432, -18.569029876991156, -2.6792003366730035],
[20.706518520569514, 18.344297403881875, 99.52244671131733, -58.53124969563653, -60.74384321042212, -80.57540534651835, 7.884792406540866, -144.33871131678563, 80.134199744324, 20.50745285622448],
[-52.88299538575159, -15.782505343805528, 16.63316001054924, 88.09475330556671, -17.619552086641818, -19.943999528182427, -120.46286026828177, 19.354752020806302, 43.49422099308949, 28.733924806541363],
[-2.4621368711159897, -1.2074759925679757, -1.5133898639835084, 2.173715352424188, -5.988707597991683, 3.0234147182203843, 3.3284199340000797, -1.8805161326360575, 5.151910934121654, -1.2540553911612116]
],
[//Layer2
[-0.3357437479474717, 0.01430651794222215, -0.7927524256670906, 0.2121636229648523, 1.0587803023358318, -3.759288325505095],
[-1.1988028704442968, 1.3768997508052783, -3.8480086358278816, 0.5289387340947143, 0.5769459339961177, -1.2528318145750772],
[1.0074966649240946, 1.155301164699459, -2.974254371052421, 0.47408176553219467, 0.5939042688615264, -0.7631976947131744]
]
];
//From (-∞,∞) to ~[-1,1]
const In1 = [(Math.abs(lat) - 26.950680212887473)/48.378128506956,(prec - 12.229929140832644)/29.94402033696607];

let lastIn = In1;
let lstOut = [];
for (let levelN = 0; levelN < weights.length ; levelN++) {
let layerN = weights[levelN];
for (let i = 0; i < layerN.length ; i++) {
lstOut[i] = 0;
for (let j = 0; j < layerN[i].length ; j++)
lstOut[i] = lstOut[i] + lastIn[j]*layerN[i][j] ;
//Sigmoid
lstOut[i] = 1/(1+Math.exp(-lstOut[i]));
}
lastIn = lstOut.slice(0);
}

//From [0,1] to [min,max]
//Standard deviation for average temperature for the year
const yearSig = lstOut[0]*62.9466411977018+0.28613807855649165;
//Standard deviation for the difference between the minimum and maximum temperatures for the year
const yearDelTmpSig = (lstOut[1]*13.541688670361175+0.1414213562373084) > yearSig ? yearSig : (lstOut[1]*13.541688670361175+0.1414213562373084);
//Expected value for the difference between the minimum and maximum temperatures for the year
const yearDelTmpMu = lstOut[2]*15.266666666666667+0.6416666666666663;

//Temperature change shape
//const formTmp = -Math.cos(data*2*Math.PI) / 2;
const delT = yearDelTmpMu/2+0.5*yearDelTmpSig/2;
const minT = temperature - ((yearSig+delT) > 15 ? (yearSig+delT) : 15);
const maxT = temperature + (temperature - minT);

const chartWidth = window.innerWidth/2,
chartHeight = 300; // height of our land/sea profile, excluding the biomes data below
const xOffset = 80,
yOffset = 10; // this is our drawing starting point from top-left (y = 0) of SVG

const xscale = d3.scaleLinear().domain([0,360]).range([0, chartWidth]);
const xscale_inv = d3.scaleLinear().domain([0, chartWidth]).range([0,360]);
const yscale = d3.scaleLinear().domain([minT, maxT]).range([chartHeight, 0]);
const yscale_inv = d3.scaleLinear().domain([chartHeight, 0]).range([minT, maxT]);

const dataAverTmp = [];
const dataMinTmp = [];
const dataMaxTmp = [];
for (let i = 0; i < 360 ; i++) {
let formTmp = Math.cos(i/360*2*Math.PI) / 2;
if(lat > 0) formTmp = -formTmp;
const averT = formTmp * yearSig + temperature;
const delT = yearDelTmpMu/2+formTmp*yearDelTmpSig/2;
dataAverTmp.push({x:xscale(i) + xOffset,y:yscale(averT) + yOffset});
dataMinTmp.push({x:xscale(i) + xOffset,y:yscale(averT-delT) + yOffset});
dataMaxTmp.push({x:xscale(i) + xOffset,y:yscale(averT+delT) + yOffset});
}

document.getElementById("epControls").style.visibility = "hidden";

$("#elevationProfile").dialog({
title: "Seasonal temperature schedule",
resizable: false,
width: window.width,
close: closeTGForBurg,
position: {my: "left top", at: "left+20 bottom-500", of: window, collision: "fit"}
});

draw();

function draw() {

document.getElementById("elevationGraph").innerHTML = "";
const legendSize = 60;
const chart = d3
.select("#elevationGraph")
.append("svg")
.attr("width", chartWidth + 120)
.attr("height", chartHeight + yOffset + legendSize)
.attr("id", "elevationSVG")
.attr("class", "epbackground");
// arrow-head definition
chart.append("defs").append("marker").attr("id", "arrowhead").attr("orient", "auto").attr("markerWidth", "2").attr("markerHeight", "4").attr("refX", "0.1").attr("refY", "2");

let Gen = d3.line().curve(d3.curveBasis).x((p) => p.x).y((p) => p.y);

//print graphs
chart.append("g").append("path").attr("d", Gen(dataAverTmp))
.attr("fill", "none").attr("stroke", "orange").on("mousemove", printVal).style("stroke-width", "2");
chart.append("g").append("path").attr("d", Gen(dataMinTmp))
.attr("fill", "none").attr("stroke", "blue").on("mousemove", printVal).style("stroke-width", "2");
chart.append("g").append("path").attr("d", Gen(dataMaxTmp))
.attr("fill", "none").attr("stroke", "red").on("mousemove", printVal).style("stroke-width", "2");

//print legend
chart.append("circle").attr("cx",chartWidth*1/4).attr("cy",chartHeight + yOffset + legendSize*0.8).attr("r", 4).style("fill", "red")
chart.append("text").attr("x", chartWidth*1/4+20).attr("y", chartHeight + yOffset + legendSize*0.8)
.text("Day temperature").style("font-size", "10px").attr("alignment-baseline","middle")
chart.append("circle").attr("cx",chartWidth*2/4).attr("cy",chartHeight + yOffset + legendSize*0.8).attr("r", 4).style("fill", "orange")
chart.append("text").attr("x", chartWidth*2/4+20).attr("y", chartHeight + yOffset + legendSize*0.8)
.text("Average daily temperature").style("font-size", "10px").attr("alignment-baseline","middle")
chart.append("circle").attr("cx",chartWidth*3/4).attr("cy",chartHeight + yOffset + legendSize*0.8).attr("r", 4).style("fill", "blue")
chart.append("text").attr("x", chartWidth*3/4+20).attr("y", chartHeight + yOffset + legendSize*0.8)
.text("Night temperature").style("font-size", "10px").attr("alignment-baseline","middle")



//print title
let timerId = setTimeout(() => chart.attr("data-tip", "Seasonal temperature schedule"), 1000);

const months = ["January","February","March","April","May","June","July","August","September","October","November","December"];
//Val under line
function printVal(){
let m = d3.mouse(this);
let tmp = convertTemperature(yscale_inv(m[1]-yOffset).toFixed(1));
let month = months[parseInt(xscale_inv(m[0]-xOffset)/360*12)];
chart.attr("data-tip", tmp + " in " + month);
clearTimeout(timerId);
timerId = setTimeout(() => chart.attr("data-tip", "Seasonal temperature schedule"), 1000);
}

const xAxis = d3
.axisBottom(xscale)
.ticks(10)
.tickFormat(function (d) {
return months[parseInt(d/360*12)];
});
const yAxis = d3
.axisLeft(yscale)
.ticks(5)
.tickFormat(function (d) {
return convertTemperature(d);
});

const xGrid = d3.axisBottom(xscale).ticks(10).tickSize(-chartHeight).tickFormat("");
const yGrid = d3.axisLeft(yscale).ticks(5).tickSize(-chartWidth).tickFormat("");

chart
.append("g")
.attr("id", "epxaxis")
.attr("transform", "translate(" + xOffset + "," + parseInt(chartHeight + +yOffset + 20) + ")")
.call(xAxis)
.selectAll("text")
.style("text-anchor", "center")
.attr("transform", function (d) {
return "rotate(0)"; // used to rotate labels, - anti-clockwise, + clockwise
});

chart
.append("g")
.attr("id", "epyaxis")
.attr("transform", "translate(" + parseInt(+xOffset - 10) + "," + parseInt(+yOffset) + ")")
.call(yAxis);

// add the X gridlines
chart
.append("g")
.attr("id", "epxgrid")
.attr("class", "epgrid")
.attr("stroke-dasharray", "4 1")
.attr("transform", "translate(" + xOffset + "," + parseInt(chartHeight + +yOffset) + ")")
.call(xGrid);

if(minT < 0 && maxT > 0){
//add zero lv
chart.append("g").append("line")
.attr("x1", xscale(0) + xOffset)
.attr("y1", yscale(0) + yOffset)
.attr("x2", xscale(360) + xOffset)
.attr("y2", yscale(0) + yOffset)
.attr("stroke", "black");
}

// add the Y gridlines
chart
.append("g")
.attr("id", "epygrid")
.attr("class", "epgrid")
.attr("stroke-dasharray", "4 1")
.attr("transform", "translate(" + xOffset + "," + yOffset + ")")
.call(yGrid);
}
function closeTGForBurg() {
document.getElementById("epControls").style.visibility = "visible";
document.getElementById("elevationGraph").innerHTML = "";
modules.elevation = false;
}
}

0 comments on commit ccb7557

Please sign in to comment.