block by dnomadb 4255f780da50bd982ed1b2eecb7b4a5c

4255f780da50bd982ed1

Full Screen

index.html

<!DOCTYPE html>
<html>
<head>
<meta name='viewport' content='initial-scale=1,maximum-scale=1,user-scalable=no' />
<title>Sweep Snap</title>

<style type="text/css">
    html { height: 100%; width: 100%}
    body {
      height: 100%;
      width: 100%;
      margin: 0;
      padding: 0;
    }
    #map_canvas {
      width: 100%;
      height: 100%; 
    }
</style>
<link href='https://www.mapbox.com/base/latest/base.css' rel='stylesheet'>
<script src='https://api.tiles.mapbox.com/mapbox.js/v1.6.3/mapbox.js'></script>
<link href='https://api.tiles.mapbox.com/mapbox.js/v1.6.3/mapbox.css' rel='stylesheet' />
</head>
<body>
    <div id="map_canvas"></div>
    <script type="text/javascript" src="general-geo-utils.js"></script>
    <script type="text/javascript" src="path-edge-detect.js"></script>
    <!--<script type="text/javascript" src="sweep-search.js"></script>worker: have to reference it in development for it to not cache-->
</body>
</html>

general-geo-utils.js

var offsetPoint = function(p1,a,d){
	var brng = a*(Math.PI/180.0);
	var R = 41807040;
	var lat1 = (Math.PI/180.0)*p1.lat;
	var lon1 = (Math.PI/180.0)*p1.lng;
	var lat2 = Math.asin( Math.sin(lat1)*Math.cos(d/R) + 
              Math.cos(lat1)*Math.sin(d/R)*Math.cos(brng));
	var lon2 = lon1 + Math.atan2(Math.sin(brng)*Math.sin(d/R)*Math.cos(lat1), 
                     Math.cos(d/R)-Math.sin(lat1)*Math.sin(lat2));
	
	return {"lat":lat2*(180.0/Math.PI),"lng":lon2*(180.0/Math.PI)}
}
var bearingDegrees = function(p1,p2){
	var dLon = (Math.PI/180.0)*((p2.lng-p1.lng));
	var lat1 = (Math.PI/180.0)*p1.lat;
	var lat2 = (Math.PI/180.0)*p2.lat;
	var y = Math.sin(dLon) * Math.cos(lat2);
	var x = Math.cos(lat1)*Math.sin(lat2) - Math.sin(lat1)*Math.cos(lat2)*Math.cos(dLon);
	var brng = Math.atan2(y, x)*(180.0/Math.PI);
	return brng;
}
var bearingRadians = function(p1,p2){
	var dLon = (Math.PI/180.0)*((p2.lng-p1.lng));
	var lat1 = (Math.PI/180.0)*p1.lat;
	var lat2 = (Math.PI/180.0)*p2.lat;
	var y = Math.sin(dLon) * Math.cos(lat2);
	var x = Math.cos(lat1)*Math.sin(lat2) - Math.sin(lat1)*Math.cos(lat2)*Math.cos(dLon);
	var brng = Math.atan2(y, x);
	return brng;
}
var latLng2tile = function(lat,lon,zoom){
	var eLng = (lon+180)/360*Math.pow(2,zoom);
	var eLat = (1-Math.log(Math.tan(lat*Math.PI/180) + 1/Math.cos(lat*Math.PI/180))/Math.PI)/2 *Math.pow(2,zoom);
	//x coord in image tile of lat/lng
	var xInd = Math.round((eLng-Math.floor(eLng))*256);
	//y coord in image tile of lat/lng
	var yInd = Math.round((eLat-Math.floor(eLat))*256);
	//flattened index for clamped array in imagedata
	var fInd = yInd*256+xInd;
	//for calling tile from array
	var eLng = Math.floor(eLng);
	var eLat = Math.floor(eLat);
	return {"tileCall":""+zoom+"/"+eLng+"/"+eLat,"tileX":eLng,"tileY":eLat,"pX":xInd,"pY":yInd,"arrInd":fInd}
}
function drawChart(divID){
	var divH = document.getElementById(divID).clientHeight,
		divW = document.getElementById(divID).clientWidth,
		chartSRC = '<iframe src="chart.html" id="chartframe" frameborder=0 height="'+divH+'" width ="'+divW+'" scrolling="no"></iframe>';
		document.getElementById(divID).innerHTML = chartSRC;
}
function distance(p1,p2){
	var R = 41807040;
	var dLat = (Math.PI/180.0)*((p2.lat-p1.lat));
	var dLon = (Math.PI/180.0)*((p2.lng-p1.lng));
	var lat1 = (Math.PI/180.0)*p1.lat,
		lat2 = (Math.PI/180.0)*p2.lat;
	var a = Math.sin(dLat/2) * Math.sin(dLat/2) + Math.sin(dLon/2) * Math.sin(dLon/2) * Math.cos(lat1) * Math.cos(lat2);
    var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
    var d = R * c;
  	return d;
}

path-edge-detect.js

var tileSize,
    locationHash,
    everyOther = true;


    var loadHash = window.location.hash.replace('#','').split('/');

    if (loadHash.length != 3){
        loadHash = [37.77872,-122.26403,18];
        history.pushState(null,null,'#'+loadHash[0]+'/'+loadHash[1]+'/'+loadHash[2])
    }

    var map = L.map('map_canvas',{
        center: [loadHash[0], loadHash[1]],
        zoom: loadHash[2],
        worldCopyJump: true,
        doubleClickZoom: false
        });

    var tiles = new L.TileLayer.Canvas({
        unloadInvisibleTiles:true,
        attribution: '<a href="http://www.mapbox.com/about/maps/" target="_blank">Terms &amp; Feedback</a>'
    });


    var worker = new Worker('sweep-search.js');

    worker.addEventListener('message', function(response) {
        // Take the 'edge' response, set the cursor
        //cursMark.setLatLng(response.data);
    }, false);

    var drawStyles = {
        color: 'red'
    }
    var hoverStyle = {
        color: 'white'
    }

    var lineArr = [L.polyline([],drawStyles)],
        startPoint = L.circleMarker([],drawStyles)
            .on('mouseover',function() {
                this.setRadius(10)
                    .setStyle(hoverStyle);
            })
            .on('mouseout',function() {
                this.setRadius(8)
                    .setStyle(drawStyles);
            })
            .on("click", endDraw),
        currPoint = L.circleMarker([],drawStyles);

    lineArr[0].addTo(map);


    function drawShape(e){
        lineArr[lineArr.length-1].addLatLng(e.latlng);
        worker.postMessage({
            'data':{
                'lat': e.latlng.lat,
                'lng': e.latlng.lng
            },
            type: 'firstpoint'
        });
        currPoint.setStyle(hoverStyle);
        setTimeout(function(){
            currPoint.setStyle(drawStyles);
        },100)
    }

    var edgePoints = L.layerGroup([])
        .addTo(map);

    worker.addEventListener('message', function(response) {
        // TODO - Edge correlation sensitive to first mouse move after click - need to work out a method to straighten.
        if (response.data.type == "movepoint"){
            lineArr[lineArr.length-1].spliceLatLngs(-1,1,response.data.data);
            currPoint.setLatLng(response.data.data);
        }
        else if (response.data.type == "edgepoint"){
            edgePoints.addLayer(L.circleMarker(response.data.data,{radius:3}));
        }
        else {
            edgePoints.clearLayers();
        }
    }, false);

    function updatePoint(e){
        worker.postMessage({
            'data':{
                'lat': e.latlng.lat,
                'lng': e.latlng.lng,
                'zoom': map.getZoom()
            },
            type: 'mouseevent'
        });
    }

    function startDraw(e) {
        lineArr[lineArr.length-1].addTo(map)
        drawShape(e);
        map.on("mousemove", updatePoint);
        currPoint
            .setLatLng(e.latlng)
            .setRadius(5)
            .addTo(map);
        startPoint
            .setLatLng(e.latlng)
            .setRadius(10)
            .addTo(map);
    }

    function endDraw() {
        lineArr[lineArr.length-1].spliceLatLngs(-1,1,startPoint.getLatLng());
        map.removeLayer(startPoint);
        map.removeLayer(currPoint);
        lineArr.push(L.polyline([],drawStyles));
        map.off('mousemove');
        map.once("click", startDraw);
    }
    map.once("click", startDraw)

    map.on("click", drawShape);

    map.on('moveend',function(){
        // Set url with lat/lng/zoom
        locationHash = '#'+''+map.getCenter().toString().replace('LatLng(','').replace(')','').replace(', ','/')+'/'+map.getZoom();
        history.replaceState(null,null,locationHash);
    });


    tiles.on('tileunload', function(e){
        //Send tile unload data to worker to delete un-needed pixel data
        worker.postMessage({'data':e.tile._tilePoint.id,'type':'tileunload'})
    });

    tiles.drawTile = function (canvas, tile, zoom) {
        tileSize = this.options.tileSize;

        var context = canvas.getContext('2d'),
            imageObj = new Image(),
            tileUID = ''+zoom+'/'+tile.x+'/'+tile.y;

        // To access / delete tiles later
        tile.id = tileUID;

        imageObj.onload = function() {
            // Draw Image Tile
            context.drawImage(imageObj, 0, 0);

            // Get Image Data
            var imageData = context.getImageData(0, 0, tileSize, tileSize);

            worker.postMessage({
                'data':{
                    'tileUID':tileUID,
                    'tileSize':tileSize,
                    'array':imageData.data},
                    'type':'tiledata'},
                [imageData.data.buffer]);

        }

        // Source of image tile
        imageObj.crossOrigin = 'Anonymous';
        imageObj.src = `https://b.tiles.mapbox.com/v4/mapbox.satellite/${zoom}/${tile.x}/${tile.y}.jpg?access_token=pk.eyJ1IjoibWF0dCIsImEiOiJTUHZkajU0In0.oB-OGTMFtpkga8vC48HjIg`     

    }

    tiles.addTo(map);

sweep-search.js

importScripts('general-geo-utils.js');

// Variables to be defined later, but needed in this context
var firstPoint,
    currBearing,
    currZoom,
    currDist,
    newBearing,
    lastMoveDist,
    kernelSamples,
    lastPoint;

// Tile Data Holder
var tileData = {};

//Interval (ft) upon which to sample
var sampInter = 4;

// Maximum Search Factor - search distance = sampInter*searchFactor;
var searchFactor = 24;
// Minimum mouse distane to re-search
var mouseMoveThreshold = 30;
// Minimum segment length before re-searching
var minimumEdgeLength = 70;
// Density on the line to sample
var searchDensity = 10;

//Listen for events
self.addEventListener('message', function(e) {

    // obect to hold various methods based on message to worker
    var edgeFind = {
        // If event is a mouse event
        mouseevent: function (inPoint) {
            var currPoint = {
                lat: inPoint.lat,
                lng: inPoint.lng
            }

            currZoom = inPoint.zoom;
            currDist = distance(currPoint,firstPoint);
            kernelSamples = Math.round(currDist/searchDensity);

            if (currDist<minimumEdgeLength) {
                // send message to set point at mouse move position
                self.postMessage({
                    type:'movepoint',
                    data:currPoint
                });
                lastMoveDist = mouseMoveThreshold+1;
                lastPoint = currPoint;
            }

            else if (lastMoveDist>mouseMoveThreshold) {
                //send message to clear edge points
                self.postMessage({
                    type:'clearedgepoints'
                });

                currBearing = bearingDegrees(currPoint,firstPoint);
                distInterval = currDist/(kernelSamples+1);
                var lastSamplePoint = firstPoint,
                    accBearing = 0,
                    accX = 0,
                    accY = 0;

                for (var k = 1;k<kernelSamples+1;k++) {
                    var tempSamplePt = offsetPoint(firstPoint,currBearing-180,k*distInterval),
                        alignKern = [0,0,0],
                        edgeAlignArr = [];
                        alignPositions = [];

                    for (var i = -1*searchFactor*sampInter;i<sampInter*searchFactor+1;i+=sampInter) {

                        var tempPoint = offsetPoint(tempSamplePt,currBearing+90,i),
                            tileInfo = latLng2tile(tempPoint.lat,tempPoint.lng,currZoom);
                        alignKern.pop();
                        alignKern.splice(0,0,tileData[tileInfo.tileCall][tileInfo.arrInd]);

                        if (i>=-1*searchFactor*sampInter+2*sampInter) {
                            edgeAlignArr.push(((searchFactor*sampInter)-Math.abs(i))*Math.abs(-1*alignKern[0]+1*alignKern[2]));
                            alignPositions.push(tempPoint);
                        }

                    }
                    var maxArrayIndex = edgeAlignArr.indexOf(Math.max.apply(null, edgeAlignArr));


                    if (maxArrayIndex!=-1) {
                        var currEdgeBearing = bearingRadians(lastSamplePoint,alignPositions[maxArrayIndex]);
                        accX+=Math.sin(currEdgeBearing);
                        accY+=Math.cos(currEdgeBearing);

                        lastSamplePoint = alignPositions[maxArrayIndex];
                        // Send message to make an "edge point"
                        self.postMessage({
                            type: 'edgepoint',
                            data: lastSamplePoint
                        });
                    }
                }

                newBearing = 180+(Math.atan2(accX,accY)*180/Math.PI);
                lastPoint = offsetPoint(firstPoint,newBearing,-1*currDist);
                lastMoveDist = 0;

            }

            else {
                newPoint = offsetPoint(firstPoint,newBearing,-1*currDist);
                // Corrected point at mean angle, and current distance
                self.postMessage({
                    type: 'movepoint',
                    data: newPoint
                });
                lastMoveDist = distance(currPoint, lastPoint);         
            }
        },

        // If it is the first click on a segment
        firstpoint: function (inPoint) {
            firstPoint = {
                lat: inPoint.lat,
                lng: inPoint.lng
            }
        },

        // If tile data was sent, add to data object
        tiledata: function (inTile) {
            var dataArray = new Uint16Array(65536);
            for (var i=0;i<inTile.array.length/4;i++) {
                // Only use R and G for speed
                var tDataVal = (inTile.array[i*4]+inTile.array[i*4+1]);
                dataArray[i] = (tDataVal);
            }
            delete inTile.array;
            tileData[inTile.tileUID] = dataArray;
        },

        // If a tile unload event was sent, delete the corresponding data
        tileunload: function (tileUnloadID) {
            delete tileData[tileUnloadID];
        }
    }
    // Call function based on message, send data.
    edgeFind[e.data.type](e.data.data);


}, false);