Skip to content

Commit

Permalink
Changes to CreateWebLoader to make it part of MatrixManager again. (#523
Browse files Browse the repository at this point in the history
)
  • Loading branch information
ChrisWakefield authored Nov 21, 2023
1 parent dd72fb9 commit f09b1e1
Show file tree
Hide file tree
Showing 3 changed files with 181 additions and 182 deletions.
1 change: 0 additions & 1 deletion NGCHM/WebContent/chm.html
Original file line number Diff line number Diff line change
Expand Up @@ -497,7 +497,6 @@ <h3 style="margin-bottom:0px;">Font &amp; Paper Options:</h3>
<script src="javascript/UserHelpManager.js"></script>
<script src="javascript/HeatMapRep.js"></script>
<script src="javascript/ColorMapManager.js"></script>
<script src="javascript/CreateWebLoader.js"></script> <!--PRESERVE closure compiler breaks this-->
<script src="javascript/MatrixManager.js"></script>
<script src="javascript/Dendrogram.js"></script>
<script src="javascript/SearchState.js"></script>
Expand Down
179 changes: 0 additions & 179 deletions NGCHM/WebContent/javascript/CreateWebLoader.js

This file was deleted.

183 changes: 181 additions & 2 deletions NGCHM/WebContent/javascript/MatrixManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,7 @@
//

// Define Namespace for NgChm MatrixManager
const MMGR = NgChm.importNS('NgChm.MMGR');

const MMGR = NgChm.createNS('NgChm.MMGR');

const UTIL = NgChm.importNS('NgChm.UTIL');
const FLICK = NgChm.importNS('NgChm.FLICK');
Expand Down Expand Up @@ -59,6 +58,186 @@ MMGR.localRepository= '/NGCHM';
form.submit();
}

//Create a worker thread to request/receive json data and tiles. Using a separate
//thread allows the large I/O to overlap extended periods of heavy computation.
MMGR.createWebLoader = function (fileSrc) {

const debug = false;
const baseURL = getLoaderBaseURL (fileSrc);

var nameLookupURL = undefined;
if (UTIL.mapNameRef !== '') {
nameLookupURL = baseURL + "GetMapByName/" + UTIL.mapNameRef;
}


// Define worker script.
// (it was seen to cause problems to include "use strict" in wS [code string passed to web worker])
let wS = `const debug = ${debug};`;
wS += `const maxActiveRequests = 2;`; // Maximum number of tile requests that can be in flight concurrently
wS += `var active = 0;`; // Number of tile requests in flight
wS += `const pending = [];`; // Additional tile requests
wS += `const baseURL = "${baseURL}";`; // Base URL to prepend to requests.
wS += `var mapId = "${UTIL.mapId}";`; // Map ID.
//wS += `const mapNameRef = "${UTIL.mapNameRef}";`; // Map name (if specified).
wS += `const nameLookupURL = "${nameLookupURL}";`


// Create a function that determines the get tile request.
// Body of function depends on the fileSrc of the NG-CHM.
wS += 'function tileURL(job){return baseURL+';
if (fileSrc === MMGR.WEB_SOURCE) {
wS += '"GetTile?map=" + mapId + "&datalayer=" + job.layer + "&level=" + job.level + "&tile=" + job.tileName';
} else {
// [bmb] Is LOCAL_SOURCE ever used? ".bin" files were obsoleted years ago.
wS += 'job.layer+"/"+job.level+"/"+job.tileName+".bin"';
}
wS += ";}";

// The following function is stringified and sent to the web loader.
function loadTile (job) {
if (active === maxActiveRequests) {
pending.push(job);
return;
}
active++;
const req = new XMLHttpRequest();
req.open("GET", tileURL(job), true);
req.responseType = "arraybuffer";
req.onreadystatechange = function () {
if (req.readyState == req.DONE) {
active--;
if (pending.length > 0) {
loadTile (pending.shift());
}
if (req.status != 200) {
postMessage({ op: 'tileLoadFailed', job });
} else {
// Transfer buffer to main thread.
postMessage({ op: 'tileLoaded', job, buffer: req.response }, [req.response]);
}
}
};
req.send();
}
wS += loadTile.toString();

// Create a function that determines the get JSON file request.
// Body of function depends on the fileSrc of the NG-CHM.
wS += 'function jsonFileURL(name){return baseURL+';
if (fileSrc === MMGR.WEB_SOURCE) {
wS += '"GetDescriptor?map=" + mapId + "&type=" + name';
} else {
wS += 'name+".json"';
}
wS += ";}";

// The following function is stringified and sent to the web loader.
function loadJson (name) {
const req = new XMLHttpRequest();
req.open("GET", jsonFileURL(name), true);
req.responseType = "json";
req.onreadystatechange = function () {
if (req.readyState == req.DONE) {
if (req.status != 200) {
postMessage({ op: 'jsonLoadFailed', name });
} else {
// Send json to main thread.
postMessage({ op:'jsonLoaded', name, json: req.response });
}
}
};
req.send();
};
wS += loadJson.toString();

// This function will be stringified and sent to the web loader.
function handleMessage(e) {
if (debug) console.log({ m: 'Worker: got message', e, t: performance.now() });
if (e.data.op === 'loadTile') { loadTile (e.data.job); }
else if (e.data.op === 'loadJSON') { loadJson (e.data.name); }
}
wS += handleMessage.toString();

// This function will be stringified and sent to the web loader.
function getConfigAndData() {
// Retrieve all map configuration data.
loadJson('mapConfig');
// Retrieve all map supporting data (e.g. labels, dendros) from JSON.
loadJson('mapData');
}
wS += getConfigAndData.toString();

// If the map was specified by name, send the code to find the
// map's id by name. Otherwise just get the map's config
// and data.

function getMapId(url) {
fetch (url)
.then (res => {
if (res.status === 200) {
res.json().then (mapinfo => {
mapId = mapinfo.data.id;
getConfigAndData();
});
} else {
postMessage({ op: 'jsonLoadFailed', name: 'GetMapByName' });
}
});
}
wS += getMapId.toString();

if (UTIL.mapId === '' && UTIL.mapNameRef !== '') {
wS += getMapId.name + '(nameLookupURL);';
} else {
wS += getConfigAndData.name + '();';
}

wS += `onmessage = ${handleMessage.name};`;
if (debug) wS += `console.log ({ m:'TileLoader loaded', t: performance.now() });`;

// Create blob and start worker.
const blob = new Blob([wS], {type: 'application/javascript'});
if (debug) console.log({ m: 'MMGR.createWebLoader', blob, wS });
MMGR.webLoader = new Worker(URL.createObjectURL(blob));
// It is possible for the web worker to post a reply to the main thread
// before the message handler is defined. Stash any such messages away
// and play them back once it has been.
const pendingMessages = [];
MMGR.webLoader.onmessage = function(e) {
pendingMessages.push(e);
};
MMGR.webLoader.setMessageHandler = function (mh) {
// Run asychronously so that the heatmap can be defined before processing messages.
setTimeout(function() {
while (pendingMessages.length > 0) {
mh (pendingMessages.shift());
}
MMGR.webLoader.onmessage = mh;
}, 0);
};

// Called locally.
function getLoaderBaseURL (fileSrc) {
var URL;
if (fileSrc == MMGR.WEB_SOURCE) {
// Because a tile worker thread doesn't share our origin, we have to pass it
// an absolute URL, and to substitute in the CFG.api variable, we want to
// build the URL using the same logic as a browser for relative vs. absolute
// paths.
URL = document.location.origin;
if (CFG.api[0] !== '/') { // absolute
URL += '/' + window.location.pathname.substr(1, window.location.pathname.lastIndexOf('/'));
}
URL += CFG.api;
} else {
console.log (`getLoaderBaseURL: fileSrc==${fileSrc}`);
URL = MMGR.localRepository+"/"+MMGR.embeddedMapName+"/";
}
return URL;
}
};


// Handle replies from tileio worker.
function connectWebLoader (heatMap, addMapConfig, addMapData) {
Expand Down

0 comments on commit f09b1e1

Please sign in to comment.