Last active
May 25, 2020 20:10
-
-
Save mrummuka/b70b9a9fb0825b4bace91a4b6d6b1257 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const MYSTERY_RADIUS = 3.218; // km | |
const CACHE_RADIUS = 0.160; // km (WAS: 0.1609) | |
// resolution for how dense the created points will be (target: one gps decimal) | |
// var cellSide = 0.00001666666667; // if I calculated correctly this should have been enough, but apparently I didn't so doubling the precision for tests | |
const cellSide = 0.000006; | |
// converts decimal degree value into array of degrees, minutes and minute decimals (gps) | |
// todo: range checks | |
function l_dd2d_m_dec(val) { | |
var valDeg, valMin, valMinDec, valDec, result = []; | |
valDeg = Math.floor( Math.abs(val) ); | |
result.push( (Math.sign(val) * valDeg).toString() ); | |
valMinDec = (( Math.abs(val) - valDeg) * 60); | |
valMin = Math.floor( valMinDec ); | |
if( valMin < 10 ) { | |
result.push( "0" + valMin.toString() ); | |
} | |
else { | |
result.push( valMin.toString() ); | |
} | |
valDec = (valMinDec - valMin).toFixed(3).substring(2,5); | |
result.push( valDec ); | |
return result; | |
} | |
function l_dd2dm(val) { | |
var valDeg, valMin, result; | |
if(val < 0) { | |
valDeg = Math.ceil( val ); | |
result = valDeg.toString(); | |
} | |
else { | |
valDeg = Math.floor( val ); | |
result = valDeg.toString(); | |
} | |
result += " "; | |
valMin = ( ( Math.abs(val) - Math.abs(valDeg) ) * 60).toFixed(3); | |
if( valMin < 10 ) { | |
result += "0"; | |
} | |
result += valMin; | |
return result; | |
} | |
function generatePoints( fc ) { | |
// only one new featurecollection for gathering all the created waypoints as currently we don't really need (do we?) separation of waypoints between different polygons (i.e. labelling of regions) | |
let wps_fc = new turf.featureCollection( [] ); | |
// generate waypoints within every hand-drawn region to wps_fc | |
// for every feature | |
fc.features.forEach( feat => { | |
// TODO: think if there should be a better way (e.g. labelling regions when drawing?) | |
// .. that is hand-drawn polygon, AND has NO description property | |
if( feat.geometry.type == "Polygon" ) { | |
// first create bounding box for each | |
let region_bbox = turf.bbox( feat ); | |
// set mask to hand-drawn polygon | |
var opt = { units: 'degrees', mask : feat }; | |
// generate set of waypoints cellSide distance apart from each other | |
var wpfc = turf.pointGrid( region_bbox, cellSide, opt); | |
// merge all features of all featurecollections to one featurecollection // PREFERRED WAY | |
// hack: just append the features to an existing array | |
wps_fc.features = wps_fc.features.concat( wpfc.features ); | |
} | |
}) | |
return wps_fc; | |
} | |
// find feature of bruteforce origo | |
function getBogus( fc ) { | |
return fc.features.find( feat => feat.properties.desc == "bruteforce" ) | |
} | |
// find all features that are cache wps (excluding bruteforce cache's bogus) | |
function getCaches1( fc ) { | |
let known_caches = fc.features.filter( feat => | |
(feat.properties.type == "Geocache|Traditional Cache" && (feat.properties.sym == "Geocache" || feat.properties.sym == "Geocache Found") ) | |
|| feat.properties.sym == "Physical Stage" | |
|| feat.properties.sym == "Final Location" | |
) | |
return known_caches; | |
} | |
// find all features that are cache wps (excluding bruteforce cache's bogus) | |
function getCaches2( fc ) { | |
let known_caches = fc.features.filter( feat => feat.properties.sym == "triangle") | |
return known_caches; | |
} | |
// remove all waypoints from generated waypoints (features) in wps_fc that are either too far from bogus or too close to other caches cache | |
// todo: ignore this to option? | |
function removeOverlapping( wps_fc, bogus, caches ) { | |
let wps_ok; | |
if( bogus == undefined ) { | |
wps_ok = wps_fc.features.filter( feat => | |
caches.find( cache => turf.distance(turf.getGeom(cache), turf.getGeom(feat)) < CACHE_RADIUS ) == undefined | |
) | |
} | |
else { | |
wps_ok = wps_fc.features.filter( feat => | |
caches.find( cache => turf.distance(turf.getGeom(cache), turf.getGeom(feat)) < CACHE_RADIUS ) == undefined && | |
turf.distance( turf.getGeom(bogus), turf.getGeom(feat) ) < MYSTERY_RADIUS | |
) | |
} | |
let wps_ok_fc = turf.featureCollection( wps_ok ); | |
return wps_ok_fc; | |
} | |
// Sheets visualization preparation: | |
// TODO: perhaps instead of coordEach simple for each feature would be better approach.. | |
// adds various gps latlon formattings as property for csv conversion and easier sheets visualization | |
// OBS! modifes feature collection object given as parameter | |
function addVisProperties( fc ) { | |
turf.coordEach( fc, function (currentCoord, coordIndex, featureIndex, multiFeatureIndex, geometryIndex) { | |
//=currentCoord | |
//=coordIndex | |
//=featureIndex | |
//=multiFeatureIndex | |
//=geometryIndex | |
// convert all dd coordinates into degress-decimalminutes and store them into properties | |
//TODO: here I want to get | |
// 1: geo.fi checker compatible seaprate vvalues | |
latgps = l_dd2d_m_dec( currentCoord[1] ); | |
fc.features[featureIndex].properties.latdeg = latgps[0]; | |
fc.features[featureIndex].properties.latmin = latgps[1]; | |
fc.features[featureIndex].properties.latdec = latgps[2]; | |
longps = l_dd2d_m_dec( currentCoord[0] ); | |
fc.features[featureIndex].properties.londeg = longps[0]; | |
fc.features[featureIndex].properties.lonmin = longps[1]; | |
fc.features[featureIndex].properties.londec = longps[2]; | |
// 2: com checker compatible one latlon string | |
fc.features[featureIndex].properties.gps = l_dd2dm(currentCoord[1]) + " " + l_dd2dm(currentCoord[0]); | |
// 3: script compatible one string with ; at end | |
// TODO | |
// 4: ?? | |
//wps_ok_fc.features[featureIndex].properties.latlongpsplain = [ l_dd2dm_plain(currentCoord[1]), l_dd2dm_plain(currentCoord[0]) ]; | |
}); | |
return fc; | |
} | |
// generate subset of features which have unique coordinates (on GPS coordinate resolution) i.e. remove unnecessary duplicates from generated wps | |
function removeDuplicates( wps_ok_fc ) { | |
let uniqueWpMap = new Map(); | |
wps_ok_fc.features.forEach( f => { | |
let crd = turf.getCoord( f ); | |
// use string of gps coordinates as key to reduce the total number of features to unique ones only | |
let gpsCrdStr = JSON.stringify( [l_dd2dm(crd[1]), l_dd2dm(crd[0]) ] ); | |
uniqueWpMap.set( gpsCrdStr, f ) | |
} ); | |
let uniqueWps = []; | |
let uniqueCrds = []; | |
// push map values (i.e. features that have been reduced so that their coordinates are unique on gps resolution) back to an array | |
uniqueWpMap.forEach( (value,key) => { | |
uniqueWps.push( value ); | |
uniqueCrds.push( key ); // for sanity check, also push the keys, i.e. gps coordinates | |
} ); | |
// and map these features back to new feature collection for geojson creation | |
let unique_wps_fc = turf.featureCollection( uniqueWps ); | |
return unique_wps_fc; | |
} | |
function generateUniqueWps( fc ) { | |
let generated = generatePoints( fc ); | |
let bogus = getBogus( fc ); | |
let caches = getCaches2( fc ); | |
let filteredWps = removeOverlapping( generated, bogus, caches ); | |
let visWps = addVisProperties( filteredWps ); | |
let uniqueWps = removeDuplicates( visWps ); | |
return uniqueWps; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment