You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
I have a geomasking task for tens of thousands of points using Turf.js. However, in JavaScript it takes a lot of time, more than 5 minutes. We have a geoprocessing machine with 48 core CPUs that I use by browser on for the masking process. That also takes the same time as my on my laptop. The answer I am told is that JavaScript is a single-threaded language, meaning that it cannot utilise more than one core at once for a given script - hence why my laptop and the geoprocessing server we have (with 48 cores) take the same time.
Therefore, no matter how much I try to give my Chrome web browser executable Real-time priority on Windows, it will not be able to use the other 3 cores of CPU of my laptop.
The code may get too complex. I would need to split the converted geojson file to multiple parts and while I am doing this, I will need to engage about 10 web workers in parallel to mask in parallel. Afterwards, the returned masked data will need to be appended to form the final masked geojson.
The masking procedure I am using is from MaskmyXYZ. It is performing geomasking using Turf.js. I think we can call webworkers from line 195 of this code https://github.com/TheTinHat/MaskMyXYZ/blob/master/assets/js/xyz.js There are many variables on the xyz.js file that need to be passed back and forth to the webworker via sockets. I have been told this can be solved by just passing a chunk of features to each worker - Just the one variable.
However, the case I have is complicated, and I just don't know where to start. I hope you understand this is a complex task, thats why I do not have much code to start with. I do know that in the following attached long script, from the place it says
turf.featureEach(sensitive.data, function (currentFeature, featureIndex)
we have to split the geojson into, say 10 parts, and call webworkers to do the masking, transferring the masking code from this script.
//From line 143 of https://github.com/TheTinHat/MaskMyXYZ/blob/master/assets/js/xyz.js
//Main masking procedure
var xyz = {
//Define random number generator function
getRandom: function (min, max) {
const randomBuffer = new Uint32Array(1);
window.crypto.getRandomValues(randomBuffer);
let randomNumber = randomBuffer[0] / (0xffffffff + 1);
randomResult = (randomNumber * (max - min)) + min;
return randomResult;
},
getRandomCurved: function(min, max) {
while (true) {
randomAttempt = this.getRandom(min, max);
probability = randomAttempt;
randomQualifier = this.getRandom(min, max);
if (randomQualifier < probability) {
return randomAttempt;
}
}
},
//Displace function is the main donut masking procedure
displace: function () {
var startTime = new Date();
//If masking has already been performed, clear any generated variables
if (masked.data.layer !== null) {
//...
}
//Test if boundary is loaded or not, and if it is then give each row an ID
if (typeof boundary.data !== 'undefined') {
boundary.isLoaded = true;
boundary.assignID();
}
//Get the user-defined distance values and convert them to meters
this.minDist = document.getElementById("minDistInput").value;
this.maxDist = document.getElementById("maxDistInput").value;
this.minDist = this.minDist / 1000;
this.maxDist = this.maxDist / 1000;
//Masking time!
turf.featureEach(sensitive.data, function (currentFeature, featureIndex) {
//Create local random distance and angle variables
var randDist;
var randAngle;
do {
var isWithinBoundary = false; //Set the boundary checker to false
randDist = xyz.getRandomCurved((xyz.minDist), (xyz.maxDist)); //generate a random distance based on user inputs
//console.log(randDist*1000)
randAngle = xyz.getRandom(0.000000, 360.000000); //generate a random angle
var currentFeatureMasked = turf.transformTranslate(currentFeature, randDist, randAngle); //move the current point according to the random distance and angle that were generated
var currentFeatureReprojected = jQuery.extend(true, {}, currentFeatureMasked); //add the now masked feature to the reprojected object (where it will get reprojected). Must do this first to add the whole object, rather than just the reprojected coordinates
currentFeatureReprojected.geometry.coordinates = proj4(projection['sensitive'], currentFeatureMasked.geometry.coordinates); //reproject the coordinates based on the projection of the original sensitive input data
// Boundary Checking
if (boundary.isLoaded == true) {
var p1 = turf.tag(currentFeature, boundary.data, "newID", "bID"); //spatial join the sensitive point to the boundary its in
var p2 = turf.tag(currentFeatureMasked, boundary.data, "newID", "bID"); //spatial join the masked point to the boundary its in
turf.tag(currentFeatureReprojected, boundary.data, "newID", "bID"); //not entirely sure this line is even necessary or does anything
//
if (p1.properties.bID == p2.properties.bID) {
isWithinBoundary = true;
masked.rawdata.push(currentFeatureMasked);
masked.rawReprojected.push(currentFeatureReprojected);
}
else {
isWithinBoundary = false;
};
}
else { //if no boundary layer is loaded, then just push the masked data into the appropriate arrays
masked.rawdata.push(currentFeatureMasked);
masked.rawReprojected.push(currentFeatureReprojected);
};
// Spruill's Measure Calculation
nearestPoint = turf.nearestPoint(currentFeatureMasked, sensitive.data)
actualDist = turf.nearestPoint(currentFeatureMasked, currentFeature)
if (nearestPoint.properties.distanceToPoint == actualDist.properties.distanceToPoint) {
spruill.push("yes");
}
} while (boundary.isLoaded == true && isWithinBoundary == false); //this keeps the procedure looping until the boundary variable is true. If no boundary is loaded, then it'll just run it once and be done.
});
masked.data = turf.featureCollection(masked.rawdata); //turn the masked data array of features into a Feature Collection
masked.reprojected = turf.featureCollection(masked.rawReprojected); //do the same as above for the reprojected version
// Process Center Calculations
beforeCenter = turf.getCoord(turf.center(sensitive.data)); //find the center of the sensitive data
afterCenter = turf.getCoord(turf.center(masked.data)); //find the center of the masked data
centerMove = turf.distance(beforeCenter, afterCenter)*1000 //calculate the distance between the sensitive and masked centers, times 1000 to get meters
$("#centerMove").html("Mean Center Displacement Distance: " + Math.round(centerMove * 100)/100 + " meters"); //update the html with the distance the center moved
endTime = new Date();
executionTime = ((endTime - startTime) / 1000);
console.log(executionTime);
},
};
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
-
I have a geomasking task for tens of thousands of points using Turf.js. However, in JavaScript it takes a lot of time, more than 5 minutes. We have a geoprocessing machine with 48 core CPUs that I use by browser on for the masking process. That also takes the same time as my on my laptop. The answer I am told is that JavaScript is a single-threaded language, meaning that it cannot utilise more than one core at once for a given script - hence why my laptop and the geoprocessing server we have (with 48 cores) take the same time.
Therefore, no matter how much I try to give my Chrome web browser executable Real-time priority on Windows, it will not be able to use the other 3 cores of CPU of my laptop.
The only way to make use of this many CPUs is to use webworkers. However, according to the following links, I would need to chunk my data (in geojson format) as well. https://superuser.com/questions/1066641/allow-browser-to-use-more-cores
The code may get too complex. I would need to split the converted geojson file to multiple parts and while I am doing this, I will need to engage about 10 web workers in parallel to mask in parallel. Afterwards, the returned masked data will need to be appended to form the final masked geojson.
The masking procedure I am using is from MaskmyXYZ. It is performing geomasking using Turf.js. I think we can call webworkers from line 195 of this code https://github.com/TheTinHat/MaskMyXYZ/blob/master/assets/js/xyz.js There are many variables on the xyz.js file that need to be passed back and forth to the webworker via sockets. I have been told this can be solved by just passing a chunk of features to each worker - Just the one variable.
There are some resources on Turf.js and webworkers https://www.charcoalstyles.com/blog/Creating-a-generic-TurfJs-webworker-with-Webpack-5/
However, the case I have is complicated, and I just don't know where to start. I hope you understand this is a complex task, thats why I do not have much code to start with. I do know that in the following attached long script, from the place it says
turf.featureEach(sensitive.data, function (currentFeature, featureIndex)
we have to split the geojson into, say 10 parts, and call webworkers to do the masking, transferring the masking code from this script.
Beta Was this translation helpful? Give feedback.
All reactions