• No results found

Detta arbete kan ses som ett förstasteg i att eskalera utvecklingen av nya bibliotek eller använda redan existerande teknologier för att ta den presenterade applikationen vidare. I denna studie har endast utritningstiden av cylindrar mätts, men en undersökning i hur mängden bilder per sekund applikationen har vid användning skulle vara mycket intressant.

En applikation skall inte enbart laddas snabbt utan det är minst lika viktigt eller kanske viktigare att applikationen beter sig på ett sätt så att den känns bra att använda. En enkät undersökning eller experiment där potentiella användare involveras och får använda applikationen skulle kunna ge en god insikt i det faktiska intresset för denna form av visualisering. Det är då möjligt att inkludera en annan undersökningsmetod för att få fram mer relevant data kopplat till faktisk användning och nytta hos slutanvändare.

Ett annat framtida arbete kan vara att utföra experiment på applikationen i en extern server miljö istället för en lokal, för att undersöka hur applikationen fungerar med access från internet. Det vore intressant och se om en bättre optimerad miljö lämpad för denna form av applikation kan ha en signifikant påverkan och förbättra prestandan hos respektive bibliotek.

Det finns flera olika lokala webbserver miljöer utöver Xampp, att undersöka dessa alternativ om det visar sig att det är mer populärt att använda denna form av applikation lokalt kan också ge en inblick i hur stor effekt miljön har på applikationen.

Det existerar också ett flertal olika imperativa samt deklarativa bibliotek och huruvida ett imperativt bibliotek är bättre än ett annat är intressant att undersöka. Att jämföra X3DOM eller ett annat deklarativt bibliotek skulle kunna presentera ett helt annat slut resultat än vad som kom upp i detta arbete. En bredare undersökning mellan fler bibliotek både deklarativa och imperativa skulle ge en ännu bättre jämförelse både inom varje tillvägagång och mot varandra.

För projekt där resurser inte vore ett hinder skulle nästa steg vara att presentera även kartan på ett 3D plan med exempelvis korrekt höjd på berg eller representera landskap nära kopplat verkliga förhållanden som väder. En 3D karta med starkare koppling till verkligheten skulle skapa många möjligheter för att visualisera data på ett sätt som gör det möjligt med simuleringar närmare verkligheten. Sådan applikation skulle kunna ha stora fördelar att sprida kunskap inom skolor för att arbeta mot myter och falsk information.

Referenser/References

Alun, E., Marco, R., Arash, B., Javi, A., Josep, B., (2014)

3D graphics on the web: A survey Computers & Graphics, 2014.

41, Pages 43-61, ISSN 0097-8493

Ahamed, S.I., Pezewski, A. & Pezewski, A. (2004) Towards framework selection criteria and suitability for an application framework. I International Conference on Information Technology: Coding and Computing, 2004. Proceedings. ITCC 2004. 5-7 april Las Vegas, USA. IEEE. s. 424–428.

Bourhis, P., Reutter, L. J., Suárez, F & Vrgoč, D. (2017). JSON: data model, query languages and schema specification. IProceedings of the 36thACMSIGMOD-SIGACT-SIGAI Symposium onPrinciples of Database Systems. Chicago, Illinois, USA 14-19 maj 2017, ss. 123-135.

Callieri, M., Leoni, C.,Dellepiane, M., & Scopigno, R., (2013). Artworks narrating a story: a modular framework for the integrated presentation of three-dimensional and textual contents, 18th ACM International Conference on 3D Web Technology, Web3D 2013; San Sebastian; Spain; 20 June 2013 through 22 June 2013, Web3D 2013: 18th International Conference on 3D Web Technology 2013, 167-176

Google Maps, 2020

https://developers.google.com/maps/ [Hämtad Maj, 2020]

Gutbell R., Pandikow L., Kuijper A. (2018) Web-Based Visualization Component for Geo-Information. In: Yamamoto S., Mori H. (eds) Human Interface and the Management of Information. Interaction, Visualization, and Analytics. HIMI 2018. Lecture Notes in Computer Science, vol 10904. Springer, Cham

Ha, Y. U., Jin, J. H., & Lee, M. J. (2015). Lets3D: A collaborative 3d editing tool based on cloud storage. International Journal of Multimedia and Ubiquitous Engineering, 10(9), 189-198.

Haara, A., Pykäläinen, J., Tolvanen, A., & Kurttila, M. (2018). Use of interactive data visualization in multi-objective forest planning.

Journal of environmental management, 210, 71-86.

Hoxmeier, J.A. & DiCesare, C. (2000). System Response Time and User Satisfaction: An Experimental Study of Browser-based Applications. AMCIS 2000 Proceedings. Long Beach, Kalifornien, USA 2000, ss.140–145.

Jankowski J, Ressler S, Jung Y, Behr J, Slusallek P. Declarative integration of interactive 3D graphics into the world-wide web: principles, current approaches, and research agenda.

In: Proceedings of the 18th international conference on 3D web technology (Web3D ;(13׳ .2013 p. 39–45.

Kaboudian, A., Cherry, E. M., Fenton, F. H, (2019). Large-scale interactive numerical experiments of chaos, solitons and fractals in real time via GPU in a web browser, Chaos, Solitons and Fractals 2019, 121, ss 6-29

Khan, M, Z., & Hashem, M. M. A. (2019). A Comparison between HTML5 and OpenGL in Rendering Fractal,

2019 International Conference on Electrical, Computer and Communication Engineering (ECCE)

Mama Nsangou Mouchili, John William Atwood, and Shadi Aljawarneh. 2019. Call data record based big data analytics for smart cities.

In Proceedings of the Second International Conference on Data Science, E-Learning and Information Systems (DATA ’19). Association for Computing Machinery, New York, NY, USA, Article 22, 1–7.

Mozilla Developer Network. (2020), https://developer.mozilla.org/en-US/ [Hämtad April, 2020]

MDNS-fetch (2020), https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API [Hämtad April, 2020]

Shadedrelief. (2020),

www.shadedrelief.com/natural3/pages/textures.html [Hämtad April, 2020]

Nolde, M., Schwanebeck, M., Dethlefsen, F. et al. Utilization of a 3D webGIS to support spatial planning regarding underground energy storage in the context of the German energy system transition at the example of the federal state of Schleswig–Holstein. Environ Earth Sci 75, 1284 (2016)

O. David, J.C. Ascough, W. Lloyd, T.R. Green, K.W. Rojas, G.H. Leavesley, L.R. Ahuja, A software engineering perspective on environmental modeling framework design: The Object Modeling System, Environmental Modelling & Software, Volume 39, 2013, Pages 201-213, ISSN 1364-8152

Okanovic, V. (2011) Designing a web application framework. 2011 18th International Conference on Systems, Signals and Image Processing (IWSSIP). June s. 1–4.

Pandas.(2020)

https://pandas.pydata.org/ [Hämtad April, 2020]

Pano, A., Graziotin, D. & Abrahamsson, P. (2018). Factors & actors leading to the adoption ofa JavaScript framework. Empirical Software Engineering, 23(6), pp.3503-3534.

P. Göth., (2020), A17pongo-EXJOBB2020, (2020), Github repository, https://github.com/redines/WEBUG-EXJOBB2020

Ressler, S., & Leber, K. (2013, November). Web Based 3D Visualization and Interaction for Whole Body Laser Scans. In Proc. of 4th Int. Conf. on 3D Body Scanning Technologies, Long Beach CA, USA(pp. 166-172).

StackOverflow., (2020),

https://stackoverflow.com/ [Hämtad April, 2020]

ThreeJs., (2020),

https://threejs.org/docs/index.html#manual/en/introduction/Creating-a-scene [Hämtad April, 2020]

W3school.,(2020),

Wohlin, C., Runeson, P., Höst, M., Ohlsson, M. C., Regnell, B., & Wesslén, A. (2012).

Experimentation in software engineering. Springer Science & Business Media.

X3DOM. (2020),

https://www.x3dom.org/ [Hämtad April, 2020]

Xampp. (2020)

https://www.apachefriends.org/index.html [Hämtad April, 2020]

Zhou, X., Wang, J., Guo, M. et al. Multimed Tools Appl (2019) 78: 28575.

Appendix A – ThreeJs HTML

<!DOCTYPE html>

<html lang="en">

<head>

<meta charset="UTF-8">

<meta name="viewport" content="width=device-width, initial-scale=1.0">

<title>THREEJS ARTEFAKT</title>

</head>

<body>

<canvas id="map"></canvas>

<!--External Javascript-->

<script src="../lib/threejs/three.js"></script>

<script src="../lib/threejs/OrbitControls.js"></script>

<script src="../lib/chance.min.js"></script>

<script src="threejs.js"></script>

</body>

</html>

Appendix B – ThreeJs JavaScript

console.log('Threejs artefact - running');

//Constant RGB color values for coloring cylinders depending on data value //RED: alot of data

//yellow: medium amount of data //green: not alot of data

const red = 'rgb(255, 0, 0)';

const yellow = 'rgb(255, 184, 0)';

const green = 'rgb(134, 234, 52)';

//Creating a global scene needed for showing anything graphics related var scene = new THREE.Scene();

var seed = new Chance(12345);

var heightARR = [];

//function for retrieving data from json file with fetch API function fetchData() {

fetch('../data/SMHI_merged_simplified_data.json') .then((response) => {

return response.json();

}) .then((data) => {

data.forEach(item => { heightARR.push(item.year) });

})

.then(function () {

//adds wanted amount of values to heightArr

//representing the same data value year in dataset from SMHI //seed is used to get the same random numbers

for (var i = 0; i < 15460; i++) {

heightARR.push(seed.floating({ min: 1, max: 1500 })) }

}).then(function () { addCylinders();

});

}

function threejs_init() {

//Setting up threejs const FOV = 45;

const WIDTH = window.innerWidth;

const HEIGHT = window.innerHeight;

const ASPECT = WIDTH / HEIGHT;

const NEAR = 0.1;

const FAR = 10000;

const camera = new THREE.PerspectiveCamera(FOV, ASPECT, NEAR, FAR);

var canvas = document.getElementById('map');

canvas.width = window.innerWidth;

canvas.height = window.innerHeight;

const renderer = new THREE.WebGLRenderer({ alpha: true, canvas: canvas });

//Adding plane for showing the map as a texture var texture = new

THREE.TextureLoader().load('../img/3_no_ice_clouds_16k_modified.jpg');

var pm = new THREE.MeshBasicMaterial({ map: texture });

var mesh = new THREE.Mesh(pg, pm);

mesh.position.y = -250;

mesh.rotation.x = -Math.PI / 2;

scene.add(mesh);

camera.position.y = 4000;

camera.position.x = 4000;

camera.position.z = 4000;

// controls

var controls = new THREE.OrbitControls(camera, renderer.domElement);

controls.maxPolarAngle = Math.PI * 0.5;

controls.minDistance = 1000;

controls.maxDistance = 5000;

controls.update();

//Animate funktion to draw 3D object to scene function animate() {

requestAnimationFrame(animate);

controls.update();

renderer.render(scene, camera);

}

animate();

fetchData();

}

//funktion for adding multiple Cylinder 3D objects to scene function addCylinders() {

localStorage.setItem('startTime', performance.now());

console.log("threejs-startTime: " + performance.now()) var material;

//bottom of sweden, change the value compared to i depending on the amount of data you want to be writen to scene

for (var i = 0; i < 1000; i++) {

if (heightARR[i] > 700) {

material = new THREE.MeshBasicMaterial({ color: red });

} else if (heightARR[i] > 500) {

material = new THREE.MeshBasicMaterial({ color: yellow });

} else {

material = new THREE.MeshBasicMaterial({ color: green });

}

var geometry = new THREE.CylinderBufferGeometry(5, 5, Math.round(heightARR[i]), 32);

var cylinder = new THREE.Mesh(geometry, material);

cylinder.position.x = seed.floating({ min: -1800, max: -800 });

cylinder.position.z = seed.floating({ min: -600, max: 2500 });

scene.add(cylinder);

}

//Middle of sweden 1

/*for (var i = 4000; i < 8000; i++) { if (heightARR[i] > 700) {

material = new THREE.MeshBasicMaterial({ color: red });

} else if (heightARR[i] < 699) {

} else {

material = new THREE.MeshBasicMaterial({ color: green });

}

var geometry = new THREE.CylinderBufferGeometry(5, 5, Math.round(heightARR[i]), 32);

var cylinder = new THREE.Mesh(geometry, material);

cylinder.position.x = seed.floating({ min: 200, max: 700 });

cylinder.position.z = seed.floating({ min: -1900, max: -600 });

scene.add(cylinder);

}

//Middle of sweden 2

for (var i = 8000; i < 12000; i++) { if (heightARR[i]> 700) {

material = new THREE.MeshBasicMaterial({ color: red });

} else if (heightARR[i] < 699) {

material = new THREE.MeshBasicMaterial({ color: yellow });

} else {

material = new THREE.MeshBasicMaterial({ color: green });

}

var geometry = new THREE.CylinderBufferGeometry(5, 5, Math.round(heightARR[i]), 32);

var cylinder = new THREE.Mesh(geometry, material);

cylinder.position.x = seed.floating({ min: -200, max: 300 });

cylinder.position.z = seed.floating({ min: -1300, max: -400 });

scene.add(cylinder);

}

//top of sweden

for (var i = 12000; i < 16000; i++) { if (heightARR[i] > 700) {

material = new THREE.MeshBasicMaterial({ color: red });

} else if (heightARR[i] < 699) {

material = new THREE.MeshBasicMaterial({ color: yellow });

} else {

material = new THREE.MeshBasicMaterial({ color: green });

}

var geometry = new THREE.CylinderBufferGeometry(5, 5, heightARR[i], 32);

var cylinder = new THREE.Mesh(geometry, material);

cylinder.position.x = seed.floating({ min: 300, max: 1900 });

cylinder.position.z = seed.floating({ min: -2600, max: -1600 });

scene.add(cylinder);

}*/

localStorage.setItem('endTime', performance.now());

localStorage.setItem('timeSum', Math.round((localStorage.getItem('endTime')) - (localStorage.getItem("startTime"))));

console.log("threejs-endtime: " + performance.now());

console.log("delta: " + localStorage.getItem("timeSum")) }

//Function for initiating scene configuration and starting to draw objects to scene threejs_init();

Appendix C – X3DOM HTML

<!DOCTYPE html>

<html lang="en">

<head>

<meta charset="UTF-8">

<meta name="viewport" content="width=device-width, initial-scale=1.0">

<script src="../lib/x3dom-1.8.1/x3dom-full.js"></script>

<link rel="stylesheet" href="../lib/x3dom-1.8.1/x3dom.css">

<title>X3DOM ARTEFAKT</title>

</head>

<body>

<x3d>

<scene width="100%" height ="100%">

<Viewpoint position="8.71025 -9.76534 9.28174" orientation="0.78101 0.32915 0.53075 1.14474" zNear="3.34706" zFar="32.17348" centerOfRotation="0.00000 0.00000 0.00000" fieldOfView="45.00000"

description="defaultX3DViewpointNode"></Viewpoint>

<shape>

<appearance>

<ImageTexture

url="../img/3_no_ice_clouds_16k_modified.jpg"><ImageTexture/>

</appearance>

<plane solid="true" size="32 32"></plane>

</shape>

<transform id="root" rotation="1 0 0 -1.57">

</transform>

</scene>

</x3d>

<script src="../lib/chance.min.js"></script>

</body>

</html>

<script src="../lib/chance.min.js"></script>

<script src="x3dom.js"></script>

</body>

</html>

Appendix D – X3DOM JavaScript

console.log('Running x3dom artefakt');

//Constant RGB color values for coloring cylinders depending on data value //RED: alot of data

//yellow: medium amount of data //green: not alot of data

const red = '#ff0000';

const yellow = '#f9b700';

const green = '#86ea34';

var seed = new Chance(12345);

var heightARR = [];

//function for retrieving data from json file with fetch API function fetchData() {

fetch('../data/SMHI_merged_simplified_data.json') .then((response) => {

return response.json();

})

.then((data) => { data.forEach(item => { heightARR.push(item.year) });

}).then(function () {

//adds wanted amount of values to heightArr

//representing the same data value year in dataset from SMHI //seed is used to get the same random numbers

for (var i = 0; i < 15460; i++) {

heightARR.push(seed.floating({ min: 1, max: 1500 })) }

}).then(function () { addCylinders();

});

}

//function for dynamicly add cylinders to scene function addCylinders(data) {

localStorage.setItem('startTime', performance.now());

console.log("threejs-startTime: " + performance.now()) //bottom of sweden

for (var i = 0; i < 1000; i++) {

var mat = document.createElement('Material');

var app = document.createElement('Appearance');

if (heightARR[i] > 700) {

mat.setAttribute('diffuseColor', red);

app.appendChild(mat);

} else if (heightARR[i] > 500) {

mat.setAttribute('diffuseColor', yellow);

app.appendChild(mat);

} else {

mat.setAttribute('diffuseColor', green);

app.appendChild(mat);

}

var t = document.createElement('Transform');

t.setAttribute('translation',

seed.floating({ min: -5.8, max: -1.8 }) + ' -0.5 ' + seed.floating({ min: -10.5, max: -3.5 })

s.appendChild(app);

t.appendChild(s);

var b = document.createElement('Cylinder');

b.setAttribute('radius', 0.01);

b.setAttribute('height', "0." + Math.round(heightARR[i]));

s.appendChild(b);

var ot = document.getElementById('root');

ot.appendChild(t);

} /*

//middle of sweden 1

for (var i = 500; i < 750; i++) { // Material Node

var mat = document.createElement('Material');

// Appearance Node

var app = document.createElement('Appearance');

if (data[i].year > 700) {

mat.setAttribute('diffuseColor', red);

app.appendChild(mat);

} else if (data[i].year > 500) {

mat.setAttribute('diffuseColor', yellow);

app.appendChild(mat);

} else {

mat.setAttribute('diffuseColor', green);

app.appendChild(mat);

}

var t = document.createElement('Transform');

t.setAttribute('translation',

xCord.floating({ min: -4.8, max: -2.8 }) + ' -0.5 ' + yCord.floating({ min: -2.5, max: 2.5 }) );

var s = document.createElement('Shape');

s.appendChild(app);

t.appendChild(s);

var b = document.createElement('Cylinder');

b.setAttribute('radius', 0.01);

b.setAttribute('height', "0."+Math.round(data[i].year));

s.appendChild(b);

var ot = document.getElementById('root');

ot.appendChild(t);

}

//middle of sweden 2

for (var i = 750; i < 1000; i++) { // Material Node

var mat = document.createElement('Material');

// Appearance Node

var app = document.createElement('Appearance');

if (data[i].year > 700) {

mat.setAttribute('diffuseColor', red);

app.appendChild(mat);

} else if (data[i].year > 500) {

mat.setAttribute('diffuseColor', yellow);

app.appendChild(mat);

} else {

var t = document.createElement('Transform');

t.setAttribute(

'translation',

xCord.floating({ min: -2.8, max: 1.8 }) + ' -0.5 ' + yCord.floating({ min: 0.5, max: 3.5 }) );

var s = document.createElement('Shape');

s.appendChild(app);

t.appendChild(s);

var b = document.createElement('Cylinder');

b.setAttribute('radius', 0.01);

b.setAttribute('height', "0."+Math.round(data[i].year));

s.appendChild(b);

var ot = document.getElementById('root');

ot.appendChild(t);

}

//top of sweden 2

for (var i = 1000; i < 1449; i++) { // Material Node

var mat = document.createElement('Material');

// Appearance Node

var app = document.createElement('Appearance');

if (data[i].year > 700) {

mat.setAttribute('diffuseColor', red);

app.appendChild(mat);

} else if (data[i].year > 500) {

mat.setAttribute('diffuseColor', yellow);

app.appendChild(mat);

else {

mat.setAttribute('diffuseColor', green);

app.appendChild(mat);

}

var t = document.createElement('Transform');

t.setAttribute(

'translation',

xCord.floating({ min: 1.8, max: 4.8 }) + ' -0.5 ' + yCord.floating({ min: 5.5, max: 7.5 }) );

var s = document.createElement('Shape');

s.appendChild(app);

t.appendChild(s);

var b = document.createElement('Cylinder');

b.setAttribute('radius', 0.01);

b.setAttribute('height', "0."+Math.round(data[i].year));

s.appendChild(b);

var ot = document.getElementById('root');

ot.appendChild(t);

}*/

localStorage.setItem('endTime', performance.now());

localStorage.setItem('timeSum', Math.round((localStorage.getItem('endTime')) - (localStorage.getItem("startTime"))));

console.log("threejs-endtime: " + performance.now());

console.log("delta: " + localStorage.getItem("timeSum")) }

fetchData();

Appendix E – Greasemonkey script

// ==UserScript==

// @name measureScript // @version 1

// @description performance test

// @include http://localhost/A17PONGO-EXJOBB2020/threejs_artefact/threejs.html // @include http://localhost/A17PONGO-EXJOBB2020/x3dom_artefakt/x3dom.html // ==/UserScript==

console.log('GM RUNNING');

//Function for saving measured data //and downloading it as a plain .txt file function saveDatatoFile() {

var artefact = '';

//sets the name of file == artefact that is running if (window.location.href.endsWith('threejs.html')) {

artefact = 'Threejs';

} else if (window.location.href.endsWith('x3dom.html')) { artefact = 'X3DOM';

}

//console.log('Creating file to save measuredata to');

var data = localStorage.getItem('measure');

var blob = new Blob([data], { type: 'text/plain' });

var url = window.URL.createObjectURL(blob);

var a = document.createElement('a');

a.href = url;

a.download = artefact + '.txt';

a.click();

}

//retrieving measured data from localstorage //calculating the difference and storing the resault function saveMeasure() {

var str = localStorage.getItem('measure');

str += ',' + localStorage.getItem('timeSum') + '\n';

localStorage.setItem('measure', str);

}

window.addEventListener(

'load', function () { (async () => {

var count = localStorage.getItem('count');

var runs = 10;

if (count == null || count == '' || count == "NaN") { count = 0;

localStorage.setItem('count', count) }

console.log('count:' + count);

if (count != runs) { setTimeout(function () { saveMeasure();

localStorage.setItem('count', ++count);

location.reload(true);

saveDatatoFile();

console.log('finished measuring, cleaning up');

count = 0;

localStorage.setItem('count', '0');

localStorage.setItem('startTime', '');

localStorage.setItem('endTime', '');

localStorage.clear();

} })();

}, false);

Appendix F – dataToJson script

import pandas as pd dataFile =

'data/SMHI_month_year_normal_61_90_precipitation_mm_ORIGINALDATA.txt' stations = 'data/SMHI_metobs_precipitationType24Hours_all_sites_ORIGINALDATA.csv'

#Read CSV file content and store in dataFrame, also converts City column in data to a new st ring with no whitespace

df = pd.read_csv(stations, encoding='utf-8', sep=';') dft = pd.read_fwf(dataFile, encoding='utf-8') print("---DF---")

#Remove unwanted height and active column, only keeping wanted columns id, namn, Latitu d and Longitude

df.drop(['Höjd (m)', 'Aktiv'], axis=1, inplace=True)

#Sort values in column Id ascending from .CSV file dfsort = df.sort_values(by=['Id'])

#Resets index to make dataframes match by index dfsort = dfsort.reset_index(drop=True)

print(dfsort.columns) print(dfsort)

print("---DFT---")

#Remove unwanted month columns from dataframe, only keeping sum data of year, id and p eriod

dft.drop(['period','jan','feb','mar','apr','maj','jun','jul','aug','sep','okt','nov','dec'], axis=1, inplac e=True)

#Sort values in column Id ascending from .TXT file dftsort = dft.sort_values(by=['klimnr'])

print(dftsort.columns) print(dftsort)

mergedData = pd.merge(dfsort,dftsort, right_index=True, left_index=True)

#Removing last not needed column after sorting and mergind with index mergedData.drop('klimnr', axis=1, inplace=True)

print(mergedData)

#write restructured data to json format

mergedData.to_json("data/SMHI_merged_simplified_data.json", force_ascii=True, orient=

"records")

#Code for reading and verifying data in finished JSON file

#js = pd.read_json("data/2018_SCB_BEFOLK_MANGD_simplified_data.json", encoding=' utf-8')

#print('---js---')

#print(js.sort_values(by=['Id']))

Related documents