/*
Copyright 2012-2025 VeloViewer. All Rights Reserved
###V159###
*/
var gridCol = '#eeeeee';
var p3dBGr, p3dBGg, p3dBGb;
var S, B;
var showGrid = false;
var firstDraw = true;
var i, j, xmin, xmax, n_x = 10,
    latMin, latMax, lngMin, lngMax,
    ymin, ymax, n_y = 1,
    zmin, zmax, x0, x1, y0, y1, z, x, y;
var viewerzoomed = 0,
    picturezoomed = 0;
var altMult = 0.45; //0.002;
var roadWidth = 50;
var detailLevel = 0.2;
var detailCutOff = 0.75,
    detailSize = 1; //1.5;
var spinXVal = 90; //270;
var spinYVal = -25;
var spinLightXVal = 60; //240;
var spinLightYVal = -30;
var gridVal;
var zPoly, maxDist;
var dragX, dragY, zoomX1, zoomY1, zoomX2, zoomY2, p3dSizeMult;
var altMultVar = 0.01;
var elevMult = 2;
var elevMult2d = null,
    elevMultTS = null;
var elevMult3d = 2;
var xPos = 0,
    yPos = 50,
    zoomAll = null,
    zoomAll2d = null,
    zoomAll3d = null,
    origza = 0,
    xm = null,
    ym = null,
    xm3d = null,
    ym3d = null,
    xm2d = null,
    ym2d = null,
    zoom = 2,
    zoom3d = 2,
    zoom2d = 2,
    svgM = {
        left: 20,
        right: 30,
        top: 50,
        bottom: 50
    },
    isCreatingImage = false,
    origSpinXVal = -10000,
    origElevMult3D = -10000,
    origElevMult2D = -10000,
    origElevMultTS = -10000,
    imageSize = {
        x: 1024,
        y: 512
    },
    gradArr = [],
    gradF = fPercent1,
    p3dReverse2DX = false;
var elevSmooth = 5;
var createImageTimer;
var isBlackBG = false;
var locked = false;
var aniInt, aniTimer;
var partNo;
var aniTime = 3000;
var aniGroup = 5;
var moveTimer;
var drawTime = 0;
var is3d = true,
    isTS = false;
var lastSimplifyMult = getParameterByName('simplify') != '' ? +getParameterByName('simplify') : 1;
var totalDist;
var redrawTimer = null;
var resizeViewTimer = null;
var show3dDistMarkers = false,
    show3dDistEndMarkers = false,
    show3dDistSelMarkers = false,
    show3dDistEndSelMarkers = false,
    show3dElevMarkers = false,
    show3dPlaceMarkers = false,
    show3dWayMarkers = false,
    show3dGradMarkers = false,
    p3dDistArr = [];
var dmg, distIds, elevIds, p3dStart, p3dEnd, placeMarkers, placeMarkerTimer, p3dds,
    noTitle = getParameterByName('noTitle') != '';

var p3dFG = '#000';
var p3dBG = '#fff';
var vvCol = '#FF032E'; //'#FF5672'; //'#D31F26';

var spinXVal3d = 90,
    spinYVal3d = -25,
    spinLightXVal3d = 60,
    spinLightYVal3d = -30,
    elevMult3d = 2;

function hypot(a, b) {
    return Math.sqrt(a * a + b * b);
}

if (hasLocalStorage && (typeof(localStorage['grid3d']) === 'undefined' || localStorage['grid3d'] == 'true') && document.getElementById('showGridCB') != null) {
    document.getElementById('showGridCB').checked = true;
}
useSVG = true; //you can also change this by hand

if (getParameterByName('units') == 'i') {
    smlLenMult = 3.2808399;
    smlLenUnit = 'ft';
    smlPaceMult = 91.44;
    smlPaceUnit = '/100yds';
    lrgLenMult = 0.000621371192;
    speedMult = 2.23693629;
    lrgLenUnit = 'mi';
    speedUnit = 'mph';
    lrgPaceMult = 1609.344;
    lrgPaceUnit = '/mi';
}
if (getParameterByName('units') == 'm') {
    smlLenMult = 1;
    lrgLenMult = 0.001;
    speedMult = 3.6;
    smlLenUnit = 'm';
    lrgLenUnit = 'km';
    speedUnit = 'km/h';
    smlPaceMult = 100;
    smlPaceUnit = '/100m';
    lrgPaceMult = 1000;
    lrgPaceUnit = '/km';
}

var cvd = function() {
    var output = '';
    var that = {};
    var oldFuncs;;

    var caller = function(prop) {
        return function() {
            var args = '',
                str = '';
            for (var i = 0; i < arguments.length; i++) {
                args += arguments[i];
                if (i !== arguments.length - 1) {
                    args += ',';
                }
            }
            str = 'ctx.' + prop + '(' + args + ');';
            output += str + '\n';
        };
    };

    that.overrideFuncs = function() {
        oldFuncs = {};
        for (var prop in CanvasRenderingContext2D.prototype) {
            if (CanvasRenderingContext2D.prototype.hasOwnProperty(prop)) {
                try {
                    var oldFunc = CanvasRenderingContext2D.prototype[prop];
                    if ((typeof oldFunc == 'function') && (prop !== 'createLinearGradient') && (prop !== 'createRadialGradient')) {
                        oldFuncs[prop] = oldFunc;
                        CanvasRenderingContext2D.prototype[prop] = caller(prop);
                    }
                } catch (e) {}
            }
        }
    }

    that.restoreFuncs = function() {
        for (var key in oldFuncs) {
            if (oldFuncs.hasOwnProperty(key)) {
                CanvasRenderingContext2D.prototype[key] = oldFuncs[key];
            }
        }
    }

    that.logCommand = function(command) {
        output += command + ';\n';
    }
    that.getOutput = function() {
        return output;
    }

    that.clearOutput = function() {
        output = '';
    };
    return that;
}();

var colScale = d3.scale.linear()
    .domain([-0.35, -0.25, -0.15, -0.075, -0.025, 0, 0.025, 0.075, 0.15, 0.25, 1, 2, 3])
    .range(['#621162', '#a346a3', '#0000FF', '#00FFFF', '#99F7E6', '#00EE00', '#E6F799', '#FFFF00', '#FF0000', '#800000', '#800000', '#eeeeee', '#333']);
var colScale2 = d3.scale.linear()
    .domain([-0.35, -0.25, -0.15, -0.075, -0.025, 0, 0.025, 0.075, 0.15, 0.25, 1, 2, 3])
    .range(['#460c46', '#823882', '#0000DD', '#00EEEE', '#79C7C6', '#00CC00', '#C6C779', '#EEEE00', '#DD0000', '#440000', '#440000', '#bbbbbb', '#333']);

var roadColour = '#7D7D7D';
var roadColourLight = '#D7D7D7';

var gradScale = d3.scale.linear()
    //.domain([d3.min(colScale.domain()), d3.max(colScale.domain().slice(0, colScale.domain().length - 3))])
    .domain([-0.25, 0.25])
    .range([0, 1]);

function shadeColor(color, percent) {
    var num = parseInt(color.slice(1), 16),
        amt = Math.round(2.55 * percent),
        R = (num >> 16) + amt,
        B = (num >> 8 & 0x00FF) + amt,
        G = (num & 0x0000FF) + amt;
    return "#" + (0x1000000 + (R < 255 ? R < 1 ? 0 : R : 255) * 0x10000 + (B < 255 ? B < 1 ? 0 : B : 255) * 0x100 + (G < 255 ? G < 1 ? 0 : G : 255)).toString(16).slice(1);
}

function animateRoute() {
    if (!is3d) {
        return;
    }
    if (typeof(safari) !== 'undefined' || typeof(vv_s1) !== 'undefined') {
        return;
    }

    aniGroup = 2; //Math.ceil(segment.xyz2.length / 50);
    partNo = 0;
    window.clearInterval(aniInt);
    aniInt = window.setInterval(function() {
        if (!is3d) {
            return;
        }

        var parts = '';
        for (var i = 0; i < aniGroup; i++) {
            if (parts != '') parts += ', ';
            parts += '.main.part' + (partNo + i); // + "[fill='none']";
        }
        d3.selectAll(parts)
            .attr('data-oldfill', function(d) {
                return this.getAttribute('fill')
            })
            .attr('data-oldstroke', function(d) {
                return this.getAttribute('stroke')
            })
            .transition()
            .attr('fill', function(d) {
                var fill = this.getAttribute('fill');
                return (fill != 'none' && fill != '' ? shadeColor(fill, 10) : 'none');
            })
            .attr('stroke', function(d) {
                var stroke = this.getAttribute('stroke');
                return (stroke != 'none' && stroke != '' ? shadeColor(stroke, 20) : 'none');
            })
            .duration(0)
            .transition()
            .attr('fill', function(d) {
                return this.getAttribute('data-oldfill');
            })
            .attr('stroke', function(d) {
                return this.getAttribute('data-oldstroke');
            })
            .duration(0)
            .delay(100);
        partNo += aniGroup;
        if (partNo > segment.xyz2.length - 1) {
            window.clearInterval(aniInt);
            window.clearTimeout(aniTimer);
            aniTimer = window.setTimeout(animateRoute, 20000);
        };
    }, 25);
}

function hideImage(startTimer) {
    document.getElementById('bigProfileImage3d').style.display = 'none';

    clearTimeout(createImageTimer);
    if (startTimer) {
        //createImageTimer = setTimeout(function () { displayImage() }, 1000);
    }
}

function getImage(pCanvasId) {
    var canvas1 = document.getElementById(pCanvasId);
    if (canvas1.getContext) {
        var ctx = canvas1.getContext("2d"); // Get the context for the canvas.
        return canvas1.toDataURL("image/png"); // Get the data as an image.
    }
    return null;
}

var ctx;

function displayImage() {
    var l_isCreatingImage = isCreatingImage;
    //if (parseFloat(d3.select('#svgWrapper svg').style('width')) < 800) {
    d3.select('#svgWrapper svg').style('width', imageSize.x + 'px');
    d3.select('#svgWrapper svg').style('height', imageSize.y + 'px');
    isCreatingImage = true;
    profileUrl = ' ';
    set3dSegment();
    isCreatingImage = l_isCreatingImage;

    var hideText = getParameterByName('hideText') == '1';
    //}

    var canvas = d3.select('#profile3dCanvas')
        .style('display', 'block')
        .style('width', d3.select('#svgWrapper svg').style('width'))
        .style('height', d3.select('#svgWrapper svg').style('height')).node();

    ctx = canvas.getContext('2d');

    ctx.beginPath();
    ctx.rect(0, 0, canvas.width, canvas.height);
    ctx.fillStyle = 'white';
    ctx.fill();

    d3.selectAll('.text2hide').remove();
    if (hideText) {
        d3.selectAll('.pbsG, .keyG').remove();
    }

    d3.selectAll('#svgWrapper svg path, #svgWrapper svg rect').style('fill', function() {
            return d3.select(this).style('fill') == null ? d3.select(this).attr('fill') : d3.select(this).style('fill')
        })
        .style('stroke', function() {
            return d3.select(this).style('stroke') == null ? d3.select(this).attr('stroke') : d3.select(this).style('stroke')
        })
        .style('stroke-width', function() {
            return d3.select(this).style('stroke-width') == null ? d3.select(this).attr('stroke-width') : d3.select(this).style('stroke-width')
        })

    canvg(canvas, d3.select('#svgWrapper svg').node().outerHTML.trim(), {
        ignoreMouse: true,
        ignoreAnimation: true
    });
    p3dInit(false);

    d3.select('#profile3dCanvas').node().svg.stop();

    p3dSizeMult = Math.max(1, parseInt(canvas.width) / 800);

    if (!hideText) {
        ctx.fillStyle = p3dFG;
        ctx.font = (p3dSizeMult * 20) + "px Arial";
        ctx.textBaseline = "top";
        ctx.fillText((typeof(segment.altName) === 'undefined' || segment.altName == null || segment.altName == '' ? segment.name : segment.altName), 10 * p3dSizeMult, 6 * p3dSizeMult);

        ctx.font = (p3dSizeMult * 15) + "px Arial";
        ctx.textAlign = "end";
        ctx.fillText(fDist(totalDist) + ' at ' + f1dp(100 * (segment.elevationArr2[p3dEnd] - segment.elevationArr2[p3dStart]) / totalDist) + '%', canvas.width - 10 * p3dSizeMult, 10);
        ctx.textAlign = "start";

        if (typeof(filtIndExtent) !== 'undefined' && typeof(segment.latLngArr2Copy) !== 'undefined' && (filtIndExtent[0] > 0 || filtIndExtent[1] < segment.latLngArr2Copy.length - 1)) {
            ctx.font = (p3dSizeMult * 10) + "px Arial";
            //ctx.textAlign = "center";
            //ctx.textBaseline = "bottom";
            var distFromStart = (typeof(segment.distanceArr2Copy) !== 'undefined' ? segment.distanceArr2Copy : segment.distanceArr2)[filtIndExtent[0]];
            var distToEnd = (typeof(segment.distanceArr2Copy) !== 'undefined' ? segment.distanceArr2Copy[segment.distanceArr2Copy.length - 1] - segment.distanceArr2Copy[filtIndExtent[1]] : segment.distanceArr2[segment.distanceArr2.length - 1] - segment.distanceArr2[filtIndExtent[1]]);

            //ctx.fillText(fDist(distFromStart) + ' from start', canvas.width - 10 * p3dSizeMult, 25 * p3dSizeMult);
            //ctx.fillText(fDist(distToEnd) + ' to end', canvas.width - 10 * p3dSizeMult, 35 * p3dSizeMult);
            ctx.fillText(fDist(distFromStart) + ' from start, ' + fDist(distToEnd) + ' to end', 12 * p3dSizeMult, /*canvas.height - */ 34 * p3dSizeMult);
        }

        /*ctx.textAlign = "start";
        ctx.fillStyle = p3dFG;
        ctx.font = (p3dSizeMult * 1.3 * 0.6) + "em Arial";
        ctx.fillText('powered by', p3dSizeMult * 177, canvas.height - 17);*/
    }
    var imgData = getImage('profile3dCanvas');

    var l_id = ((segment.type == 'activity' || segment.type == 'route' || segment.type == 'routes') ? (typeof(id) === 'undefined' ? 0 : id) : (typeof(activity) === 'undefined' ? (typeof(segmentId) === 'undefined' ? id : segmentId) : activity.id));
    if ((!isCreatingImage || origSpinXVal != spinXVal || (is3d ? origElevMult3D != elevMult3d : origElevMult2D != elevMult2d)) && !locked && !hideText && l_id > 0) {
        var newId = 0;
        if (is3d) {
            $.post('/api/save3D.php', {
                    id: l_id,
                    type: (segment.type == 'activity' || segment.type == 'route' || segment.type == 'routes' ? segment.type : (typeof(activity) === 'undefined' ? 'segment' : 'act')),
                    name: (typeof(segment.altName) === 'undefined' || segment.altName == null || segment.altName == '' ? segment.name : segment.altName),
                    latlon: segment.latlng,
                    elevation: segment.elevation,
                    distance: segment.distance,
                    elevMult: elevMult,
                    spinX: spinXVal,
                    spinY: spinYVal,
                    spinLightX: spinLightXVal,
                    spinLightY: spinLightYVal,
                    d2: 0
                })
                .done(function(data) {
                    newId = parseInt(data);
                    $.post('/save/save3D.php', {
                            id: l_id,
                            aid: (typeof(activity) !== 'undefined' ? activity.athleteId : 0),
                            pref: (segment.type == 'activity' || segment.type == 'route' ? 'r' : (segment.type == 'routes' ? 'rs' : (typeof(activity) === 'undefined' ? 's' : 'a'))),
                            imgdata: imgData.substr(imgData.indexOf(',') + 1).toString()
                        });
                });
            origElevMult3D = elevMult;
        } else {
            $.post('/api/save3D.php', {
                id: l_id,
                elevMult: elevMult,
                type: (segment.type == 'activity' || segment.type == 'route' || segment.type == 'routes' ? segment.type : (typeof(activity) === 'undefined' ? 'segment' : 'act')),
                d2: 1
            });
            $.post('/save/save2D.php', {
                id: l_id,
                aid: (typeof(activity) !== 'undefined' ? activity.athleteId : 0),
                pref: (segment.type == 'activity' || segment.type == 'route' ? 'r' : (segment.type == 'routes' ? 'rs' : (typeof(activity) === 'undefined' ? 's' : 'a'))),
                imgdata: imgData.substr(imgData.indexOf(',') + 1).toString()
            });
            origElevMult2D = elevMult;
        }

        origSpinXVal = spinXVal;
        $('#pleaseWaitDialog').modal('hide');
    }
    if (isCreatingImage) {
        d3.selectAll('#profileImg').attr('src', imgData);
        isCreatingImage = false;
    }
    set3dSegment();

    canvas.style.display = 'none';

    /*var imgSrc = 'http://s3.veloviewer.com/' + (is3d ? 3 : 2) + 'd/' + (segment.type == 'activity' || segment.type == 'route' ? 'r' + id : (typeof(activity) === 'undefined' ? 's' + (typeof(segmentId) === 'undefined' ? id : segmentId) : 'a' + activity.id)) + '.png';
    d3.select('#getImage' + (is3d ? '' : '2D') + 'Btn')
      .attr('href', imgSrc);

    d3.selectAll('.profile' + (is3d ? '' : '2D') + 'Img').attr('src', imgData);*/

}

function deg2rad(deg) {
    return deg * (Math.PI / 180)
}

function distLatLon(lat1, lon1, lat2, lon2, R) {
    R = typeof(R) === 'undefined' ? 6377160 : R; //6371000;
    var dLat = deg2rad(lat2 - lat1);
    var dLon = deg2rad(lon2 - lon1);
    var a =
        Math.sin(dLat / 2) * Math.sin(dLat / 2) +
        Math.cos(deg2rad(lat1)) * Math.cos(deg2rad(lat2)) *
        Math.sin(dLon / 2) * Math.sin(dLon / 2);
    var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
    var d = R * c;
    return d;
}

function setDistElevMarkers() {
    if ((show3dDistMarkers || show3dDistEndMarkers || show3dDistSelMarkers || show3dDistEndSelMarkers || show3dElevMarkers || show3dPlaceMarkers || show3dGradMarkers || show3dWayMarkers) && dmg) {
        dmg.attr('transform', d3.select('#profile3dsvg .Scene').attr('transform'))

        var dmt = dmg.selectAll('.distMarkerText')
            .data(d3.selectAll('.distMarker')[0].map(function(d) {
                    var dVal = d.attributes['d'].value.split(' ');
                    if (show3dGradMarkers) {
                        var t = d.className.baseVal.replace('main part distMarker dm', '').split('_');
                        return {
                            x: parseFloat(dVal[4]),
                            y: parseFloat(dVal[5]),
                            g: parseFloat(t[0]) / 10,
                            d: parseFloat(t[1])
                        }
                    } else {
                        return {
                            x: parseFloat(dVal[4]),
                            y: parseFloat(dVal[5]),
                            distElv: parseFloat(d.className.baseVal.replace('main part distMarker dm', '').replace('_', '.'))
                        }
                    }
                }),
                function(d) {
                    return d.x + '_' + d.y
                });

        if (show3dWayMarkers) {
            dmt.enter()
                .append('g')
                .classed('distMarkerText', true);
            dmt.html(function(d) {
                var w = segment.waymarkers.filter(function(e) {
                    return isWaymarkerShown(e) && segment.distanceArr2Copy[filtIndExtent[0]] <= e.d[0] && segment.distanceArr2Copy[filtIndExtent[1]] >= e.d[0]
                })[d.distElv];
                
                var iconSvg = getWaymarkerIconUrl(w, 'svg');

                /*if (typeof(w.optionsCode) !== 'undefined') {
                    iconSvg = waymarkers[w.type]['svg_' + w.optionsCode + '_' + w.optionsColor];
                }*/
                return '<g transform="scale(0.33)">' + iconSvg + '</g>';
            });

            dmt.attr('transform', function(d) {
                return 'translate(' + (d.x - 10) + ',' + (d.y - 20) + ')'
            });
        } else {
            var t = dmt.enter()
                .append('text')
                .classed('distMarkerText', true)
                .attr('text-anchor', 'middle')
                .style('font-size', (markerFontSize * Math.max(1, parseInt(d3.select('#profile3dsvg').style('width')) / 1000)) + 'px')
                .style('font-family', "Verdana")
                .attr('fill', p3dFG)
                .attr('z-index', 2);

            t.append('svg:tspan')
                .attr('x', function(d) {
                    return d.x
                })
                .attr('dy', 0)
                .text(function(d) {
                    return (show3dDistMarkers || show3dDistEndMarkers || show3dDistSelMarkers || show3dDistEndSelMarkers ?
                        (d.distElv == 0 ? (show3dDistMarkers || show3dDistSelMarkers ? 'Start' : 'End') : d.distElv + lrgLenUnit) :
                        (show3dElevMarkers ?
                            fInt(d.distElv * smlLenMult) + smlLenUnit : show3dGradMarkers ? f1dp(d.g) + '%' :
                            (show3dPlaceMarkers ?
                                placeMarkers.filter(function(e) {
                                    return e.lat >= minLat && e.lat <= maxLat && e.lng >= minLng && e.lng <= maxLng
                                })[d.distElv].name :
                                segment.waymarkers.filter(function(e) {
                                    return isWaymarkerShown(e) && filtIndExtent[0] <= e.distI[0].i && filtIndExtent[1] >= e.distI[0].i
                                })[d.distElv].type)))
                })
            if (show3dGradMarkers) {
                t.append('svg:tspan')
                    .attr('x', function(d) {
                        return d.x
                    })
                    .attr('dy', '1em')
                    .text(function(d) {
                        return (d.d > 1000 ? f1dp(d.d * lrgLenMult) + lrgLenUnit : d.d > 500 ? f2dp(d.d * lrgLenMult) + lrgLenUnit : fInt(d.d * smlLenMult) + smlLenUnit)
                    });
            }
            dmt.attr('x', function(d) {
                    return show3dWayMarkers ? d.x - 10 : d.x
                })
                .attr('y', function(d) {
                    return show3dWayMarkers ? d.y - 20 : d.y - (show3dGradMarkers ? 12 : 3)
                });
        }
        /*            .text(function(d) {
                        return (show3dDistMarkers || show3dDistEndMarkers || show3dDistSelMarkers || show3dDistEndSelMarkers ? (d.distElv == 0 ? (show3dDistMarkers || show3dDistSelMarkers ? 'Start' : 'End') : d.distElv + lrgLenUnit) : (show3dElevMarkers ? fInt(d.distElv * smlLenMult) + smlLenUnit : show3dGradMarkers ? f1dp(d.g) + '%' + ' ' + (d.d > 500 ? f2dp(d.d * lrgLenMult) + lrgLenUnit : fInt(d.d * smlLenMult) + smlLenUnit) : placeMarkers.filter(function(e) {
                            return e.lat >= minLat && e.lat <= maxLat && e.lng >= minLng && e.lng <= maxLng
                        })[d.distElv].name))
                    });*/


        dmt.exit().remove();
    }
}

function p3dSetDistScale() {
    if (!is3d && p3dds) {
        p3dds.attr('transform', d3.select('#profile3dsvg .Scene').attr('transform'))

        var dmt = p3dds.selectAll('text.distScaleTickText')
            .data(d3.selectAll('.distScaleTick')[0].map(function(d) {
                    var dVal = d.attributes['d'].value.split(' ');
                    return {
                        x: parseFloat(dVal[1]),
                        y: parseFloat(dVal[2]),
                        distElv: parseFloat(d.className.baseVal.replace('main part distScaleTick dm', '').replace('_', '.'))
                    }
                }),
                function(d) {
                    return d.x + '_' + d.y
                });

        dmt.enter()
            .append('text')
            .classed('distScaleTickText', true)
            .attr('text-anchor', 'middle')
            .style('font-size', (10 * Math.min(1, parseInt(d3.select('#profile3dsvg').style('width')) / 600)) + 'px')
            .style('font-family', "Verdana")
            .attr('fill', p3dFG)
            .attr('z-index', 2)
            .text(function(d) {
                return d.distElv + lrgLenUnit
            });

        dmt.attr('x', function(d) {
                return d.x
            })
            .attr('y', function(d) {
                return d.y + 16
            });

        dmt.exit().remove();
    }
}

function addPoly3D2(className, x0, x1, y0, y1, z0, z1, grad, zmin, isShadow, isTop, title, isGrid) {
    if (typeof(vv_s1) !== 'undefined' && isShadow) {
        return;
    }

    var hanging = false;
    var hangingHeight = Math.sqrt(zmax - zmin);
    if (getParameterByName('hanging') != '') {
        hanging = true;
    }

    isGrid = typeof(isGrid) === 'undefined' ? false : isGrid;
    var col = colScale(grad);

    if (isShadow) {
        z0 = zmin - (z0 - zmin);
        z1 = zmin - (z1 - zmin);
    }

    var id = zPoly.length;

    if (is3d) {
        zPoly[id] = new Poly3D(className, S, col, col, colScale2(grad), 1, isShadow, isGrid, title);
        zPoly[id].AddPoint(x0, y0, z0);
        zPoly[id].AddPoint(x1, y1, z1);
        zPoly[id].AddPoint(x1, y1, hanging ? z1 - (isShadow ? 1 : -1) * hangingHeight : zmin);
        zPoly[id].AddPoint(x0, y0, hanging ? z0 - (isShadow ? 1 : -1) * hangingHeight : zmin);
        zPoly[id].Update();

        col = grad > 0.15 ? roadColour : roadColourLight;
        id = zPoly.length;
        zPoly[id] = new Poly3D(className, S, col, col, col, 1, isShadow, isGrid, title);
        zPoly[id].AddPoint(x0, y0, z0);
        zPoly[id].AddPoint(x1, y1, z1);
        zPoly[id].Update();
    } else {
        zPoly[id] = new Poly3D(className, S, col, col, colScale2(grad), 1, isShadow, isGrid, title);
        zPoly[id].AddPoint(x0, y0, z0);
        zPoly[id].AddPoint((!isTop ? x1 : x0), y1, (!isTop ? z1 : z0));
        zPoly[id].AddPoint(x1, y1, (!isTop ? zmin : z1));
        zPoly[id].AddPoint((!isTop ? x0 : x1), y0, (!isTop ? zmin : z1));
        zPoly[id].Update();
    }
}

function p3dGetDistTickGap() {
    var dExt = d3.extent(segment.da3);
    var tickGap = 1 / lrgLenMult;
    if (totalDist <= 2.1 / lrgLenMult) {
        tickGap = 0.1 / lrgLenMult;
    } else {
        if (totalDist <= 5.1 / lrgLenMult) {
            tickGap = 0.25 / lrgLenMult;
        } else {
            if (totalDist <= 10.1 / lrgLenMult) {
                tickGap = 0.5 / lrgLenMult;
            } else {
                var distTicks = d3.scale.linear().domain([0, (dExt[1] - dExt[0]) * lrgLenMult]).ticks(parseInt(d3.select('#profile3dsvg').style('width')) / 75).filter(function(d) {
                    return parseInt(d) == d
                });
                tickGap = (distTicks[1] - distTicks[0]) / lrgLenMult;
            }
        }
    }
    return tickGap;
}

function p3dInit(p_supress) {
    var svgRect = d3.select('#profile3dsvgContainer  #svgWrapper').node().getBoundingClientRect();
    if (svgRect.width == 0) {
        setTimeout(p3dInit, 250);
        return;
    }
    if (embed && (typeof(map) !== 'undefined' || typeof(map3) !== 'undefined')) {
        setExtent();
    }

    segment.distance2 = typeof(segment.distanceArr2Copy) !== 'undefined' ? segment.distanceArr2Copy[segment.distanceArr2Copy.length - 1] : segment.distanceArr2[segment.distanceArr2.length - 1];
    if (is3d) {
        elevMult3d = elevMult;
    }

    zPoly = [];
    if (!SVGObjects || SVGObjects.length == 0) {
        setTimeout("p3dInit()", 100);
        return;
    }
    d3.selectAll('#profile3dsvg .Scene *').remove();
    S = new Scene3D(SVGObjects[0], 0, $('#profile3dsvg').width(), $('#profile3dsvg').height());

    altMult = elevMult;

    gradArr = [];

    for (i = 0; i < segment.xyz2.length - 1; i++) {
        x0 = segment.xyz2[i].x;
        x1 = segment.xyz2[i + 1].x;
        y0 = segment.xyz2[i].y;
        y1 = segment.xyz2[i + 1].y;
        z0 = segment.xyz2[i].z;
        z1 = segment.xyz2[i + 1].z;
        var grad = typeof(p3dpGrad) !== 'undefined' ? p3dpGrad(segment.xyz2[i], segment.xyz2[i + 1]) : (z1 - z0) / (segment.xyz2[i + 1].d - segment.xyz2[i].d);
        if (isNaN(grad)) {
            grad = (z1 - z0) / distLatLon(segment.xyz2[i].obj.lat, segment.xyz2[i].obj.lng, segment.xyz2[i + 1].obj.lat, segment.xyz2[i + 1].obj.lng);
        }
        z0 = (z0 - zmin);
        z1 = (z1 - zmin);
        z0 *= altMult;
        z1 *= altMult;

        var title = gradF(grad) + ' for ' + f3dp((segment.xyz2[i + 1].d - segment.xyz2[i].d) * lrgLenMult) + ' ' + lrgLenUnit + (lrgLenUnit != 'km' ? ' (' + fInt((segment.xyz2[i + 1].d - segment.xyz2[i].d) * smlLenMult) + ' ' + smlLenUnit + ')' : '');
        segment.xyz2[i].grad = grad;
        segment.xyz2[i].dist = segment.xyz2[i + 1].d - segment.xyz2[i].d;

        gradArr.push({
            i: i,
            grad: grad,
            len: segment.xyz2[i + 1].d - segment.xyz2[i].d,
            dist: segment.xyz2[i].d
        });

        if (is3d) {
            addPoly3D2('main part' + i, x0, x1, y0, y1, z0, z1, grad, -0, false, false, title);
            addPoly3D2('shadow part' + i, x0, x1, y0, y1, z0, z1, grad, -0, true, false);
        } else {
            addPoly3D2('main part' + i, x0, x1, -totalDist / 150, -totalDist / 150, z0, z1, grad, -0, false, false, title);
            addPoly3D2('main part' + i, x0, x1, totalDist / 150, totalDist / 150, z0, z1, grad, -0, false, false, title);
            addPoly3D2('part' + i, x0, x1, totalDist / 150, -totalDist / 150, z0, z1, grad, -0, false, true, title);
            if (i == 0) {
                addPoly3D2('part' + i, x0, x0, -totalDist / 150, totalDist / 150, z0, z0, 2, -0, false, false);
            }
            if (i == segment.xyz2.length - 2) {
                addPoly3D2('part' + i, x1, x1, -totalDist / 150, totalDist / 150, z1, z1, 2, -0, false, false);
            }
        }
    }

    if (!is3d || document.getElementById('showGridCB') == null || document.getElementById('showGridCB').checked) {
        // draw grid
        //1609.344 miles or 1000 for km
        showGrid = document.getElementById('showGridCB') == null || document.getElementById('showGridCB').checked;
        if (is3d) {
            gridVal = 1;
            var gridSize = 1000;
            if (lrgLenUnit != 'km') {
                gridSize = 1609.344;
            }
            var xGridLines = Math.abs((xmax - xmin) / gridSize);
            var yGridLines = Math.abs((ymax - ymin) / gridSize);

            while (Math.max(xGridLines, yGridLines) < 2) {
                gridSize = gridSize / 2;
                gridVal = gridVal / 2;
                var xGridLines = Math.abs((xmax - xmin) / gridSize);
                var yGridLines = Math.abs((ymax - ymin) / gridSize);
            }
            while (Math.min(xGridLines, yGridLines) > 20) {
                if (gridVal == 1 || gridVal == 10 || gridVal == 100 || gridVal == 1000 || gridVal == 10000) {
                    gridSize = gridSize * 2.5;
                    gridVal = gridVal * 2.5;
                } else {
                    gridSize = gridSize * 2;
                    gridVal = gridVal * 2;
                }
                var xGridLines = Math.abs((xmax - xmin) / gridSize);
                var yGridLines = Math.abs((ymax - ymin) / gridSize);
            }

            if (!is3d) {
                yGridLines = Math.max(2, yGridLines);
            }

            d3.select('#gridSize').text(gridVal + ' ' + lrgLenUnit);

            z0 = 0;
            z1 = 0;

            xminAdj = xmin - (Math.ceil(xGridLines) * gridSize - (xmax - xmin)) / 2;
            yminAdj = ymin - (Math.ceil(yGridLines) * gridSize - (ymax - ymin)) / 2;

            if (!is3d) {
                xminAdj = 0;
            }

            for (var i = 0; i <= Math.ceil(xGridLines); i++) {
                x0 = xminAdj + i * gridSize;
                x1 = x0;
                y0 = yminAdj;
                y1 = yminAdj + gridSize * Math.ceil(yGridLines);
                var id = zPoly.length;
                zPoly[id] = new Poly3D('xGrid' + i, S, gridCol, gridCol, gridCol, 1, false, true);
                zPoly[id].AddPoint(x0, y0, z0);
                zPoly[id].AddPoint(x1, y1, z1);
                zPoly[id].Update();
            }

            for (var i = 0; i <= Math.ceil(yGridLines); i++) {
                x0 = xminAdj;
                x1 = xminAdj + gridSize * Math.ceil(xGridLines);
                y0 = yminAdj + i * gridSize;
                y1 = y0;

                var id = zPoly.length;
                zPoly[id] = new Poly3D('yGrid' + i, S, gridCol, gridCol, gridCol, 1, false, true);
                zPoly[id].AddPoint(x0, y0, z0);
                zPoly[id].AddPoint(x1, y1, z1);
                zPoly[id].Update();
            }
        } else {
            p3dSetDistanceMarkers(2, 1, true);

            if (!p3dReverse2DX) {
                p3dDistArr = p3dDistArr.filter(function(d) {
                    return d <= segment.distanceArr2.last()
                });
            }
            z2 = -totalDist / 300;
            var full = false;
            if (!p3dReverse2DX && (typeof(filtIndExtent) === 'undefined' || (filtIndExtent[0] == 0 && filtIndExtent[1] == segment.distanceArr2Copy.length - 1))) {
                var full = true;
                if (segment.distanceArr2[0] < 0) {
                    addPoly3D2('', 0, -segment.distanceArr2[0], totalDist / 150, totalDist / 150, z2, z2, 1, -0, false, false, null, true);
                    addPoly3D2('', 0, -segment.distanceArr2[0], -totalDist / 150, -totalDist / 150, z2, z2, 1, -0, false, false, null, true);
                    addPoly3D2('', 0, 0, -totalDist / 150, totalDist / 150, z2, z2, 1, -0, false, false, null, true);
                }
            }
            var startDist = (full ? Math.min(segment.distanceArr2[0], 0) : 0);
            p3dDistArr.forEach(function(d, i) {
                if (i < p3dDistArr.length - 1) {
                    x0 = d - startDist;
                    x1 = p3dDistArr[i + 1] - startDist;
                    if (p3dReverse2DX) {
                        x0 = segment.distanceArr2.last() - segment.distanceArr2[0] - x0;
                        x1 = segment.distanceArr2.last() - segment.distanceArr2[0] - x1;
                    }
                    var c = i % 2 == 0 ? 2 : 3;
                    addPoly3D2('main part distScaleTick dm' + (Math.round(100 * (d * lrgLenMult)) / 100).toString().replace('.', '_'), x0, x1, -totalDist / 150, -totalDist / 150, z2, z2, c, -0, false, false, null, true);
                    addPoly3D2('', x0, x1, totalDist / 150, totalDist / 150, z2, z2, c, -0, false, false, null, true);
                    if (i == 0 && segment.distanceArr2[0] >= 0) {
                        addPoly3D2('', x0, x0, -totalDist / 150, totalDist / 150, z2, z2, 2, -0, false, false, null, true);
                    }
                    if (i == p3dDistArr.length - 2) {
                        var ed = segment.xyz2[segment.xyz2.length - 1].d;
                        if (d < ed) {
                            x0 = p3dDistArr[i + 1] - startDist;
                            x1 = ed;
                            if (p3dReverse2DX) {
                                x0 = segment.distanceArr2.last() - segment.distanceArr2[0] - x0;
                                x1 = segment.distanceArr2.last() - segment.distanceArr2[0] - x1;
                            }
                            var c = (i + 1) % 2 == 0 ? 2 : 3;
                            addPoly3D2('main part distScaleTick dm' + (Math.round(100 * (p3dDistArr[i + 1] * lrgLenMult)) / 100).toString().replace('.', '_'), x0, x1, -totalDist / 150, -totalDist / 150, z2, z2, c, -0, false, false, null, true);
                            addPoly3D2('', x0, x1, totalDist / 150, totalDist / 150, z2, z2, c, -0, false, false, null, true);
                        }
                        addPoly3D2('', x1, x1, -totalDist / 150, totalDist / 150, z2, z2, c, -0, false, false, null, true);
                    }
                }
            })
        }
    } else {
        showGrid = false;
    }

    if (location.hash == '') {
        S.AutoCenter();
    } else {
        S.AutoCenter();
        S.Center.x = d3.max(segment.xyz2, function(d) {
            return d.x
        }) / 2;
        S.Center.y = d3.max(segment.xyz2, function(d) {
            return d.y
        }) / 2;
        S.Center.z = 0;
    }

    if (is3d) {
        //draw Arrow
        var id = zPoly.length;
        zPoly[id] = new Poly3D('arrow1', S, gridCol, gridCol, gridCol, 2, false, true);
        zPoly[id].AddPoint(xmin - xmax / 6.5, ymax, 0);
        zPoly[id].AddPoint(xmin - xmax / 6.5, ymax - xmax / 10, 0);
        zPoly[id].Update();
        var id = zPoly.length;
        zPoly[id] = new Poly3D('arrow2', S, gridCol, gridCol, gridCol, 2, false, true);
        zPoly[id].AddPoint(xmin - xmax / 6.5, ymax, 0);
        zPoly[id].AddPoint(xmin - xmax / 7, ymax - xmax / 20, 0);
        zPoly[id].Update();
        var id = zPoly.length;
        zPoly[id] = new Poly3D('arrow3', S, gridCol, gridCol, gridCol, 2, false, true);
        zPoly[id].AddPoint(xmin - xmax / 6.5, ymax, 0);
        zPoly[id].AddPoint(xmin - xmax / 6, ymax - xmax / 20, 0);
        zPoly[id].Update();
        //draw N
        var id = zPoly.length;
        zPoly[id] = new Poly3D('n1', S, gridCol, gridCol, gridCol, 2, false, true);
        zPoly[id].AddPoint(xmin - xmax / 8, ymax, 0);
        zPoly[id].AddPoint(xmin - xmax / 8, ymax - xmax / 10, 0);
        zPoly[id].Update();
        var id = zPoly.length;
        zPoly[id] = new Poly3D('n2', S, gridCol, gridCol, gridCol, 2, false, true);
        zPoly[id].AddPoint(xmin - xmax / 8, ymax, 0);
        zPoly[id].AddPoint(xmin - xmax / 11, ymax - xmax / 10, 0);
        zPoly[id].Update();
        var id = zPoly.length;
        zPoly[id] = new Poly3D('n3', S, gridCol, gridCol, gridCol, 2, false, true);
        zPoly[id].AddPoint(xmin - xmax / 11, ymax, 0);
        zPoly[id].AddPoint(xmin - xmax / 11, ymax - xmax / 10, 0);
        zPoly[id].Update();
    }

    /*d3.max(segment.xyz2, function(d) {
        return d.z
      }) / 2;*/
    /*    if (origza==0) {
        origza = S.ZoomAll;
      } else {
        zoomAll = zoomAll * origza / S.ZoomAll;
      }
      S.ZoomAll = (zoomAll == null ? S.ZoomAll * zoom : zoomAll);*/

    /*    S.ZoomAll *= zoom;
      S.YM = (ym == null ? S.YM + yPos : ym);
      S.XM = (xm == null ? S.XM + xPos : xm);*/

    var stickH = Math.max((zmax - zmin) / 2, Math.max(xmax - xmin, ymax - ymin) / 20); //altMult*(zmax-zmin)/(200*(zmax-zmin)/(is3d ? Math.max((xmax-xmin),(ymax-ymin)) : totalDist));

    if (show3dDistMarkers || show3dDistEndMarkers || show3dDistSelMarkers || show3dDistEndSelMarkers) {
        p3dSetDistanceMarkers(show3dDistMarkers ? 0 : show3dDistEndMarkers ? 1 : show3dDistSelMarkers ? 2 : 3, stickH, false)
    }

    if (show3dGradMarkers) {
        var gradArr2 = [];
        gradArr.sort(function(a, b) {
            return Math.abs(b.grad / (b.grad < 0 ? 1.5 : 1)) - Math.abs(a.grad / (a.grad < 0 ? 1.5 : 1))
        });
        var c = parseInt(d3.select('#profile3dsvg').style('width')) / 100;
        for (var i = 0; i < gradArr.length; i++) {
            gradArr2.push(gradArr[i]);
            gradArr = gradArr.filter(function(d) {
                return Math.abs((gradArr[i].dist + gradArr[i].len / 2) - (d.dist + d.len / 2)) > totalDist / 20
            }).sort(function(a, b) {
                return Math.abs(b.grad / (b.grad < 0 ? 2 : 1)) - Math.abs(a.grad / (a.grad < 0 ? 2 : 1))
            });
            i = -1;
            if (gradArr2.length >= c) {
                i = 1000000;
            }
        }
        gradArr2.forEach(function(d, i) {
            x0 = segment.xyz2[d.i].x + (segment.xyz2[d.i + 1].x - segment.xyz2[d.i].x) / 2;
            y0 = segment.xyz2[d.i].y + (segment.xyz2[d.i + 1].y - segment.xyz2[d.i].y) / 2;
            z0 = segment.xyz2[d.i].z + (segment.xyz2[d.i + 1].z - segment.xyz2[d.i].z) / 2;
            //z1 = z0 + stickH; //Math.min((zmax - zmin) / 4, 1000*(zmax-zmin)/totalDist);
            z0 = (z0 - zmin);
            z0 *= altMult;
            z1 = z0 + stickH;
            //z1 *= altMult;
            var id = zPoly.length;
            zPoly[id] = new Poly3D('main part distMarker dm' + Math.round(d.grad * 1000) + '_' + Math.round(d.len), S, gridCol, gridCol, gridCol, 1, false, false);
            zPoly[id].AddPoint(x0, y0, z0);
            zPoly[id].AddPoint(x0, y0, z1);
            zPoly[id].Update();
        });
    }

    if (show3dElevMarkers) {
        var minElvGap = (zmax - zmin) / 4;
        var maxElvGap = (zmax - zmin) / 11;
        var minIds = [];
        elevIds = [];
        elevIds.push(segment.xyz2[0]);
        for (var i = 1; i < segment.xyz2.length - 1; ++i) {
            if (segment.xyz2[i - 1].z < segment.xyz2[i].z && segment.xyz2[i].z > segment.xyz2[i + 1].z) {
                if (segment.xyz2[i].z > elevIds[elevIds.length - 1].z + minElvGap || segment.xyz2[i].z < elevIds[elevIds.length - 1].z - minElvGap) {
                    elevIds.push(segment.xyz2[i]);
                    elevIds[elevIds.length - 1].type = 'max';
                } else {
                    if (segment.xyz2[i].z > elevIds[elevIds.length - 1].z) {
                        if (elevIds.length > 1) {
                            elevIds[elevIds.length - 1] = segment.xyz2[i];
                            elevIds[elevIds.length - 1].type = 'max';
                        } else {
                            elevIds.push(segment.xyz2[i]);
                            elevIds[elevIds.length - 1].type = 'max';
                        }
                    }
                }
            }
            if (segment.xyz2[i - 1].z > segment.xyz2[i].z && segment.xyz2[i].z < segment.xyz2[i + 1].z) {
                if (segment.xyz2[i].z < elevIds[elevIds.length - 1].z - maxElvGap || segment.xyz2[i].z > elevIds[elevIds.length - 1].z + maxElvGap) {
                    elevIds.push(segment.xyz2[i]);
                    elevIds[elevIds.length - 1].type = 'min';
                } else {
                    if (elevIds[elevIds.length - 1].type == 'min' && segment.xyz2[i].z < elevIds[elevIds.length - 1].z) {
                        elevIds[elevIds.length - 1] = segment.xyz2[i];
                        elevIds[elevIds.length - 1].type = 'min';
                    }
                }
            }
        }
        elevIds.push(segment.xyz2[segment.xyz2.length - 1]);

        d3.merge([elevIds, minIds]).forEach(function(d, i) {
            x0 = d.x;
            y0 = d.y;
            z0 = d.z;
            //z1 = z0 + Math.min((zmax - zmin) / 2, Math.min(200, 0.01 * (segment.da3[segment.da3.length - 1] - segment.da3[0])));
            z0 = (z0 - zmin);
            z0 *= altMult;
            //z1 = (z1 - zmin);
            //z1 *= altMult * (is3d ? 1 : 5);
            z1 = z0 + stickH;
            var id = zPoly.length;
            zPoly[id] = new Poly3D('main part distMarker dm' + d.z.toString().replace('.', '_'), S, gridCol, gridCol, gridCol, 1, false, false);
            zPoly[id].AddPoint(x0, y0, z0);
            zPoly[id].AddPoint(x0, y0, z1);
            zPoly[id].Update();
        });
    }

    if (show3dPlaceMarkers && is3d && typeof(placeMarkers) !== 'undefined') {
        placeMarkers.filter(function(d) {
                return d.lat >= minLat && d.lat <= maxLat && d.lng >= minLng && d.lng <= maxLng
            })
            .forEach(function(d, i) {
                x0 = distLatLon(d.lat, minLng, d.lat, d.lng);
                y0 = distLatLon(minLat, d.lng, d.lat, d.lng);
                //z0 = zmin;
                //z1 = zmax;
                z0 = (typeof(d.s0) === 'undefined' ? 0 : d.s0);
                //z0 *= altMult;
                //z1 = (z1 - zmin);
                //z1 *= altMult * (is3d ? 1 : 5);
                z1 = z0 + (typeof(d.sh) === 'undefined' ? stickH : d.sh);
                var id = zPoly.length;
                zPoly[id] = new Poly3D('main part distMarker dm' + i, S, gridCol, gridCol, gridCol, 1, false, false);
                zPoly[id].AddPoint(x0, y0, z0);
                zPoly[id].AddPoint(x0, y0, z1);
                zPoly[id].Update();
            });

    }
    if (show3dWayMarkers && typeof(segment.waymarkers) !== 'undefined') {
        segment.waymarkers.filter(function(d) {
                return isWaymarkerShown(d) && segment.distanceArr2Copy[filtIndExtent[0]] <= d.d[0] && segment.distanceArr2Copy[filtIndExtent[1]] >= d.d[0]
            })
            .forEach(function(d, i) {
                if (is3d) {
                    x0 = distLatLon(d.lat, minLng, d.lat, d.lng);
                    y0 = distLatLon(minLat, d.lng, d.lat, d.lng);
                } else {
                    x0 = d.d[0] - segment.distanceArr2[0];
                    y0 = 0;
                }
                z0 = segment.elevationArr2Copy[d.distI[0].i] - zmin;
                z0 *= altMult;
                z1 = z0 + (typeof(d.sh) === 'undefined' ? stickH : d.sh);
                var id = zPoly.length;
                zPoly[id] = new Poly3D('main part distMarker dm' + i, S, gridCol, gridCol, gridCol, 1, false, false);
                zPoly[id].AddPoint(x0, y0, z0);
                zPoly[id].AddPoint(x0, y0, z1);
                zPoly[id].Update();
            });

    }

    S.ChangeViewer(spinYVal, spinXVal);
    S.ChangeLight(spinLightYVal, spinLightXVal);
    S.OrderWeight.z = 0.01;
    //S.Dist *= 0.8 * 0.8;

    var startTime = new Date().getTime();
    S.Sort();
    S.Draw();
    drawTime = new Date().getTime() - startTime;

    if (getParameterByName('zoom') == '') {
        p3dSizeMult = Math.max(1, parseInt(d3.select('#profile3dsvg').style('width')) / 800);
    } else {
        p3dSizeMult = parseFloat(getParameterByName('zoom'));
    }

    resizeView();

    d3.selectAll('#profile3dsvg g:not(.Scene)').remove();

    if (show3dDistMarkers || show3dDistEndMarkers || show3dDistSelMarkers || show3dDistEndSelMarkers || show3dElevMarkers || show3dPlaceMarkers || show3dGradMarkers || show3dWayMarkers) {
        dmg = d3.select('#profile3dsvg')
            .append('g')
            .attr('class', 'distMarkersG');

        setDistElevMarkers();
    }
    if (!is3d) {
        p3dds = d3.select('#profile3dsvg')
            .append('g')
            .attr('class', 'distScaleG');
        p3dSetDistScale();
    }

    if (typeof(mapProxy) === 'undefined' || mapProxy == '' || mapProxy == 'http://' || mapProxy == 'https://') {

        if (getParameterByName('zoom') == '' && typeof(embed) !== 'undefined' && embed) {
            d3.selectAll('.title3d').remove();
            var textG = d3.select('#profile3dsvg')
                .insert("g", ":first-child");

            if (typeof(noTitle) === 'undefined' || !noTitle) {
                textG.append('text')
                    .classed('title title3d text2hide', true)
                    .attr('dominant-baseline', 'hanging')
                    .attr("transform", "translate(4,4)")
                    .style('font-size', (typeof(embed) === 'undefined' ? null : (p3dSizeMult * 2) + 'em'))
                    .style('fill', (typeof(embed) === 'undefined' ? null : p3dFG))
                    .text((typeof(segment.altName) === 'undefined' || segment.altName == null || segment.altName == '' ? segment.name : segment.altName));
            }

            textG.append('text')
                .classed('title title3d text2hide', true)
                .attr('text-anchor', 'end')
                .attr('dominant-baseline', 'hanging')
                .attr("transform", 'translate(' + ($('#profile3dsvg').width() - 4) + ',25)')
                .style('font-size', (typeof(embed) === 'undefined' ? null : (p3dSizeMult * 1.5) + 'em'))
                .style('fill', (typeof(embed) === 'undefined' ? null : p3dFG))
                .text(f1dp(lrgLenMult * totalDist) + lrgLenUnit + ' at ' + f1dp(100 * (segment.elevationArr2[p3dEnd] - segment.elevationArr2[p3dStart]) / totalDist) + '%');

            /*textG.append('text')
                .classed('title title3d text2hide visible-phone', true)
                .attr('text-anchor', 'end')
                .attr('dominant-baseline', 'hanging')
                .attr("transform", 'translate(' + ($('#profile3dsvg').width() - 4) + ',25)')
                .style('font-size', (typeof(embed) === 'undefined' ? null : (p3dSizeMult * 1.5) + 'em'))
                .style('fill', (typeof(embed) === 'undefined' ? null : p3dFG))
                .text(f1dp(lrgLenMult * totalDist) + lrgLenUnit);
            textG.append('text')
                .classed('title title3d text2hide visible-phone', true)
                .attr('dominant-baseline', 'hanging')
                .attr('text-anchor', 'end')
                .attr("transform", 'translate(' + ($('#profile3dsvg').width() - 4) + ',' + (25 + p3dSizeMult * 15) + ')')
                .style('font-size', (typeof(embed) === 'undefined' ? null : (p3dSizeMult * 1.5) + 'em'))
                .style('fill', (typeof(embed) === 'undefined' ? null : p3dFG))
                .text(f1dp(100 * (segment.elevationArr2[p3dEnd] - segment.elevationArr2[p3dStart]) / totalDist) + '%');*/
        }

        var scale = (p3dSizeMult * (isCreatingImage ? 1 : 0.5));
        var keyG = d3.select('#profile3dsvg')
            .append("g")
            .classed('keyG', true)
            .attr("transform", function(d) {
                //if (parseFloat(d3.select('body').style('width')) < 768) {
                //    return "translate(" + ($('#profile3dsvg').width() - 5) + "," + ($('#profile3dsvg').height() - 54) + ") scale(0.5, 0.5)";
                //} else {
                return "translate(" + ($('#profile3dsvg').width() - (scale * 17)) + "," + ($('#profile3dsvg').height() - (scale * 107)) + ") scale(" + scale + ", " + scale + ")";
                //}
            });

        keyG.append('linearGradient')
            .attr('id', 'g1')
            .attr('gradientUnits', 'userSpaceOnUse')
            .attr('x1', '0')
            .attr('y1', '100')
            .attr('x2', '0')
            .attr('y2', '0')
            .selectAll('stop')
            .data(colScale.domain().slice(1, colScale.domain().length - 3))
            .enter()
            .append('stop')
            .attr('offset', function(d) {
                return gradScale(d);
            })
            .attr('stop-color', function(d, i) {
                return colScale2(d)
            });

        keyG.append('rect')
            .attr('width', '10')
            .attr('height', '100')
            .attr('fill', 'url(#g1)');

        keyG.selectAll('text')
            .data([-0.25, -0.1, 0, .1, .25])
            .enter()
            .append('text')
            .style({
                'fill': p3dFG,
                'font-size': '10px'
            })
            .classed('tick', true)
            .attr('transform', function(d) {
                return 'translate(-5,' + (103 - gradScale(d) * 100) + ')';
            })
            .attr('text-anchor', 'end')
            .text(function(d, i) {
                return (is3d && showGrid && i == 0 ? '(Grid: ' + gridVal + ' ' + lrgLenUnit + ') ' : '') + (d * 100) + '%';
            });

        var logoCont = d3.select('#profile3dsvg')
            .append('g')
            .classed('pbsG', true)
            .attr("transform", function() {
                return "translate(2," + (parseInt($('#profile3dsvg').height())) + ") scale(" + scale + ", " + scale + ")";
            });


        // powered by STRAVA
        logoCont.append('g').attr('transform', 'translate(0,-48)').html('<g transform="translate(230,7.5)"><defs><style>.cls-1{fill:#fc4c02;}.cls-2{fill:#999;}</style></defs><path class="cls-1" d="M14.62,34.12a14.15,14.15,0,0,1-4.31-.64,9.54,9.54,0,0,1-3.44-1.91l2.7-3.21a8,8,0,0,0,2.59,1.36,9.31,9.31,0,0,0,2.7.41,2.13,2.13,0,0,0,1-.17,0.53,0.53,0,0,0,.3-0.47v0a0.63,0.63,0,0,0-.44-0.54,7.69,7.69,0,0,0-1.65-.45q-1.27-.26-2.43-0.61a8.35,8.35,0,0,1-2-.88A4.27,4.27,0,0,1,8.21,25.6a3.69,3.69,0,0,1-.52-2v0a4.78,4.78,0,0,1,.42-2,4.57,4.57,0,0,1,1.23-1.62,5.85,5.85,0,0,1,2-1.08,8.9,8.9,0,0,1,2.75-.39A12.87,12.87,0,0,1,18,19a9.18,9.18,0,0,1,3,1.55l-2.46,3.41a7.57,7.57,0,0,0-2.28-1.13,7.93,7.93,0,0,0-2.26-.36,1.56,1.56,0,0,0-.83.17,0.51,0.51,0,0,0-.27.45v0a0.62,0.62,0,0,0,.41.52,7,7,0,0,0,1.6.45,22.37,22.37,0,0,1,2.64.62,7.8,7.8,0,0,1,2,.94A4.16,4.16,0,0,1,20.83,27,3.81,3.81,0,0,1,21.29,29v0a4.69,4.69,0,0,1-.48,2.14,4.57,4.57,0,0,1-1.34,1.61,6.35,6.35,0,0,1-2.09,1A9.87,9.87,0,0,1,14.62,34.12Z"/><path class="cls-1" d="M25.46,23H21V18.74H35V23H30.51V33.84H25.46V23Z"/><path class="cls-1" d="M35.67,18.74H43a10.1,10.1,0,0,1,3.33.46,5.54,5.54,0,0,1,2.1,1.26,4.61,4.61,0,0,1,1,1.55,5.48,5.48,0,0,1,.35,2v0a4.77,4.77,0,0,1-.8,2.8,5.5,5.5,0,0,1-2.18,1.81l3.52,5.14H44.64l-2.85-4.32H40.72v4.32H35.67V18.74Zm7.23,7.19a2.32,2.32,0,0,0,1.42-.39,1.28,1.28,0,0,0,.52-1.08v0a1.23,1.23,0,0,0-.52-1.09,2.44,2.44,0,0,0-1.4-.36h-2.2v3H42.9Z"/><polygon class="cls-1" points="79.61 27.34 82.91 33.84 87.75 33.84 79.61 17.79 71.48 33.84 76.32 33.84 79.61 27.34"/><polygon class="cls-1" points="56.98 27.34 60.27 33.84 65.11 33.84 56.98 17.79 48.85 33.84 53.69 33.84 56.98 27.34"/><polygon class="cls-1" points="68.3 25.23 65.01 18.74 60.17 18.74 68.3 34.79 76.43 18.74 71.59 18.74 68.3 25.23"/><path class="cls-2" d="M7.55,13.15V7.55H9.79A2,2,0,0,1,11.17,8a1.65,1.65,0,0,1,.53,1.3,1.65,1.65,0,0,1-.51,1.28,2,2,0,0,1-1.39.47H8.5v2.08H7.55Zm0.94-3H9.77A1,1,0,0,0,10.46,10a0.81,0.81,0,0,0,.27-0.64,0.81,0.81,0,0,0-.28-0.68,1.1,1.1,0,0,0-.69-0.22H8.5V10.2Z"/><path class="cls-2" d="M16,13.11a2.51,2.51,0,0,1-1.8,0,2.23,2.23,0,0,1-1.32-1.44,4.25,4.25,0,0,1-.2-1.36,3,3,0,0,1,.71-2.12,2.26,2.26,0,0,1,1.7-.75,2.24,2.24,0,0,1,1.69.76,3,3,0,0,1,.71,2.12,4.22,4.22,0,0,1-.2,1.36,2.33,2.33,0,0,1-.54.93A2.25,2.25,0,0,1,16,13.11Zm-1.93-1.22a1.29,1.29,0,0,0,2.06,0,2.56,2.56,0,0,0,.4-1.58,2.34,2.34,0,0,0-.41-1.49,1.28,1.28,0,0,0-2,0,2.33,2.33,0,0,0-.41,1.48A2.56,2.56,0,0,0,14.12,11.89Z"/><path class="cls-2" d="M19.7,13.15l-1.07-5.6h1l0.76,3.94h0L21.7,7.55h0.54l1.32,3.94h0l0.76-3.94h0.94l-1.07,5.6h-1L22,9.31h0L20.7,13.15h-1Z"/><path class="cls-2" d="M26.83,13.15V7.55h3.49V8.42H27.78V9.86H30.1V10.7H27.78v1.58h2.54v0.87H26.83Z"/><path class="cls-2" d="M32.08,13.15V7.55h2.23A2,2,0,0,1,35.7,8a1.62,1.62,0,0,1,.52,1.28,1.63,1.63,0,0,1-.34,1.06,1.69,1.69,0,0,1-.91.57l1.21,2.23h-1L34,11H33v2.15H32.08Zm0.94-3h1.22a0.85,0.85,0,0,0,1-.89,0.85,0.85,0,0,0-1-.89H33v1.78Z"/><path class="cls-2" d="M37.78,13.15V7.55h3.49V8.42H38.73V9.86H41V10.7H38.73v1.58h2.54v0.87H37.78Z"/><path class="cls-2" d="M43,13.15V7.55h1.83a2.44,2.44,0,0,1,.9.17,2.37,2.37,0,0,1,.77.5,2.3,2.3,0,0,1,.55.88,3.58,3.58,0,0,1,.2,1.25,3.88,3.88,0,0,1-.21,1.34,2,2,0,0,1-.57.88,2.38,2.38,0,0,1-.78.45,2.8,2.8,0,0,1-.92.14H43ZM44,12.28H44.8a1.41,1.41,0,0,0,1.09-.42,2.26,2.26,0,0,0,.39-1.51,2.25,2.25,0,0,0-.39-1.45,1.28,1.28,0,0,0-1-.47H44v3.86Z"/><path class="cls-2" d="M51.49,13.15V7.55h2.3A1.65,1.65,0,0,1,55,8,1.41,1.41,0,0,1,55.43,9a1.17,1.17,0,0,1-.65,1.08v0a1.6,1.6,0,0,1,.6.48,1.3,1.3,0,0,1,.25.82q0,1.71-2.06,1.71h-2.1Zm0.94-3.3h1.23a0.93,0.93,0,0,0,.62-0.18,0.64,0.64,0,0,0,.22-0.52,0.66,0.66,0,0,0-.22-0.54,0.94,0.94,0,0,0-.62-0.18H52.43V9.85Zm0,2.43h1.4a1.7,1.7,0,0,0,.34-0.06,0.7,0.7,0,0,0,.28-0.14,0.72,0.72,0,0,0,.17-0.25,1,1,0,0,0,.07-0.39,0.66,0.66,0,0,0-.31-0.65,2.16,2.16,0,0,0-1-.16h-1v1.65Z"/><path class="cls-2" d="M58.23,13.15V10.79L56.4,7.55h1l1.28,2.38h0L60,7.55h1l-1.83,3.24v2.36H58.23Z"/></g><g transform="translate(2,3) scale(1.4)"><g transform="matrix(1.25,0,0,-1.25,0,29.999997)"><g transform="matrix(1.760792,0,0,1.760792,-12.151382,-8.963022)"><path transform="translate(30.6738,8.6094)" d="m 0,0 c -1.061,0 -1.82,0.887 -1.82,1.933 l 0,0.03 c 0,1.045 0.696,1.916 1.788,1.916 1.061,0 1.837,-0.887 1.837,-1.946 l 0,-0.033 C 1.805,0.871 1.092,0 0,0 m -0.032,-2.549 c 2.125,0 3.798,1.218 4.451,2.891 L 4.421,0.35 3.555,1.871 C 3.131,2.629 2.985,2.885 2.657,3.469 2.001,4.633 1.047,6.309 1.047,6.309 0.714,6.38 0.363,6.412 0,6.412 c -2.739,0 -4.733,-2.043 -4.733,-4.479 l 0,-0.033 c 0,-2.437 1.979,-4.449 4.701,-4.449 m -7.471,0.207 2.992,0 0,12.158 -2.992,0 0,-12.158 z m -5.588,4.987 c 0.158,0.949 0.696,1.55 1.487,1.55 0.824,0 1.394,-0.601 1.504,-1.55 l -2.991,0 z m 1.757,-5.194 c 1.694,0 2.881,0.697 3.704,1.742 L -9.292,0.57 c -0.618,-0.601 -1.171,-0.871 -1.9,-0.871 -0.933,0 -1.615,0.477 -1.852,1.41 l 5.731,0 c 0.016,0.205 0.016,0.428 0.016,0.618 0,2.517 -1.362,4.685 -4.307,4.685 -2.532,0 -4.321,-1.978 -4.321,-4.479 l 0,-0.033 c 0,-2.643 1.9,-4.449 4.591,-4.449" style="fill:' + p3dFG + ';stroke:none"></path><path transform="translate(6.9233,14.8223)" d="M 0,0 4.879,-8.619 9.742,0 6.852,0 4.879,-3.465 2.907,0 0,0 z" style="fill:' + p3dFG + ';stroke:none"></path><path transform="translate(32.9336,14.8223)" d="M 0,0 4.879,-8.619 9.742,0 6.852,0 4.879,-3.465 2.907,0 0,0 z" style="fill:#FF032E;stroke:none"></path><path transform="translate(72.4004,6.2676)" d="m 0,0 3.008,0 0,2.676 c 0,1.963 0.887,2.865 2.453,2.865 l 0.254,0 0,3.167 C 4.291,8.77 3.498,8.01 3.008,6.838 l 0,1.726 L 0,8.564 0,0 z m -5.508,4.986 c 0.158,0.95 0.696,1.551 1.488,1.551 0.823,0 1.393,-0.601 1.504,-1.551 l -2.992,0 z m 1.756,-5.193 c 1.695,0 2.883,0.697 3.705,1.742 l -1.662,1.377 c -0.617,-0.601 -1.172,-0.871 -1.9,-0.871 -0.934,0 -1.614,0.477 -1.852,1.41 l 5.731,0 c 0.015,0.205 0.015,0.428 0.015,0.617 0,2.518 -1.361,4.686 -4.305,4.686 -2.533,0 -4.322,-1.979 -4.322,-4.48 l 0,-0.032 c 0,-2.643 1.899,-4.449 4.59,-4.449 m -13.896,0.143 2.564,0 1.426,4.464 1.392,-4.464 2.596,0 2.645,8.628 -2.897,0 -1.156,-4.337 -1.33,4.369 -2.469,0 -1.33,-4.337 -1.125,4.305 -2.928,0 2.612,-8.628 z m -7.122,5.05 c 0.159,0.95 0.698,1.551 1.489,1.551 0.824,0 1.392,-0.601 1.504,-1.551 l -2.993,0 z m 1.758,-5.193 c 1.694,0 2.881,0.697 3.705,1.742 l -1.662,1.377 c -0.619,-0.601 -1.172,-0.871 -1.9,-0.871 -0.934,0 -1.615,0.477 -1.852,1.41 l 5.731,0 c 0.015,0.205 0.015,0.428 0.015,0.617 0,2.518 -1.361,4.686 -4.306,4.686 -2.533,0 -4.323,-1.979 -4.323,-4.48 l 0,-0.032 c 0,-2.643 1.901,-4.449 4.592,-4.449 M -30.373,0 l 2.992,0 0,8.564 -2.992,0 0,-8.564 z" style="fill:#FF032E;stroke:none"></path><path transform="translate(43.5332,15.4336)" d="M 0,0 C 0.826,0 1.496,0.67 1.496,1.496 1.496,2.322 0.826,2.992 0,2.992 -0.827,2.992 -1.496,2.322 -1.496,1.496 -1.496,0.67 -0.827,0 0,0" style="fill:#FF032E;stroke:none"></path></g></g></g>');

    }


    //white background to avoid transparent pngs
    d3.select('#profile3dsvg')
        .insert('g', ':first-child')
        .append('rect')
        .style('fill', p3dBG)
        .style('stroke-width', '0px')
        .attr('width', d3.select('#profile3dsvg').style('width'))
        .attr('height', d3.select('#profile3dsvg').style('height'));


    /*d3.select('#profile3dsvg')
        .append("g")
        .classed('hidden-phone', true)
        .append('text')
        .style('font-size', '3em')
        .style('fill', 'black')
        .style('fill-opacity', '0.2')
        .attr("transform", "translate(0," + (parseInt($('#profile3dsvg').height()) - 10) + ")")
        .text('© VeloViewer.com');*/

    d3.select('#profile3dsvgContainer')
        .on('mousedown', function(e) {
            var source = (d3.event.srcElement ? d3.event.srcElement : d3.event.originalTarget).tagName.toUpperCase();
            if (d3.select('#profile3dsvg').style('display') != 'none' && source != 'IMG' && source != 'BUTTON' && d3.event.button == 0) {
                d3.select('#profile3dsvgContainer')
                    .on('mousemove', function(f) {
                        if (d3.event.which == 1) {
                            handleMove3d(this, d3.mouse(this), 'mouse');
                        } else {
                            d3.select('#profile3dsvgContainer')
                                .on('mousemove', null);
                            dragX = null;
                            dragY = null;
                            zoomX1 = null;
                            zoomY1 = null;
                            zoomX2 = null;
                            zoomY2 = null;
                            S.Sort();
                            S.Draw();
                            setDistElevMarkers();
                            p3dSetDistScale();
                            hideImage(true);
                        }
                        d3.event.preventDefault();
                    });
                d3.event.preventDefault();
            }
        })
        .on('mouseup', function() {
            var source = (d3.event.srcElement ? d3.event.srcElement : d3.event.originalTarget).tagName.toUpperCase();
            if (d3.select('#profile3dsvg').style('display') != 'none' && source != 'IMG' && source != 'BUTTON' && d3.event.button == 0) {
                d3.select('#profile3dsvgContainer')
                    .on('mousemove', null);
                dragX = null;
                dragY = null;
                zoomX1 = null;
                zoomY1 = null;
                zoomX2 = null;
                zoomY2 = null;
                S.Sort();
                S.Draw();
                setDistElevMarkers();
                p3dSetDistScale();
                hideImage(true);
                d3.event.preventDefault();
            }
        })
        .on('touchstart', function(e) {
            var source = (d3.event.srcElement ? d3.event.srcElement : d3.event.originalTarget).tagName.toUpperCase();
            if (d3.select('#profile3dsvg').style('display') != 'none' && source != 'IMG' && source != 'BUTTON') {
                d3.select('#profile3dsvgContainer')
                    .on('touchmove', function(f) {
                        handleMove3d(this, d3.touches(this), 'touch');
                        d3.event.preventDefault();
                    });
                d3.event.preventDefault();
            }
        })
        .on('touchend', function() {
            var source = (d3.event.srcElement ? d3.event.srcElement : d3.event.originalTarget).tagName.toUpperCase();
            if (d3.select('#profile3dsvg').style('display') != 'none' && source != 'IMG' && source != 'BUTTON') {
                d3.select('#profile3dsvgContainer')
                    .on('mousemove', null);
                dragX = null;
                dragY = null;
                zoomX1 = null;
                zoomY1 = null;
                zoomX2 = null;
                zoomY2 = null;
                S.Sort();
                S.Draw();
                setDistElevMarkers();
                p3dSetDistScale();
                d3.event.preventDefault();
                hideImage(true);
            }
        });

    if (typeof(embed) === 'undefined' || location.hash != '') {
        d3.select('#profile3dsvgContainer').on('mousewheel', function(e) {
                mouseZoom(d3.event.wheelDeltaY);
                d3.event.preventDefault();
                return false;
            })
            .on('wheel', function(e) {
                mouseZoom((navigator.userAgent.indexOf("Firefox") > -1 ? -10 : -1) * d3.event.deltaY);
                d3.event.preventDefault();
                return false;
            });
    }

    if (document.getElementById('transparencyCB') != null && document.getElementById('transparencyCB').checked) {
        d3.selectAll('.Scene path').attr('opacity', 0.667);
    }

    if (p_supress == null && typeof(pageType) !== 'undefined' && pageType == 'route' && profileUrl == '') {
        displayImage();
    }

    window.clearTimeout(aniTimer);
    if (!spinning) {
        aniTimer = window.setTimeout(animateRoute, 2000);
    }
}

function p3dSetDistanceMarkers(type, stickH, isInit) {
    // type = 0: show3dDistMarkers, 1: show3dDistEndMarkers, 2: show3dDistSelMarkers, 3: show3dDistEndSelMarkers
    if (isInit) {
        p3dDistArr = [];
    }
    kz = typeof(kz) === 'undefined' ? 0 : kz;
    var tickGap = p3dGetDistTickGap();
    distTicks = d3.range(0, totalDist * lrgLenMult, tickGap * lrgLenMult)
    if (type == 0) {
        distTicks = d3.range(kz > 0 ? 0 : (segment.da3[0] + (segment.da3[0] % tickGap == 0 ? 0 : tickGap - segment.da3[0] % tickGap)) * lrgLenMult, (segment.da3[segment.da3.length - 1] + tickGap - segment.da3[segment.da3.length - 1] % tickGap) * lrgLenMult, tickGap * lrgLenMult)
        distTicks = distTicks.filter(function(d) {
            return segment.distanceArr2Copy ? d >= segment.distanceArr2Copy[filtIndExtent[0]] * lrgLenMult : true;
        });
    }
    if (type == 1) {
        var da3r = segment.da3.map(function(d) {
            return segment.distance2 - d
        }).reverse();
        distTicks = d3.range(kz > 0 ? 0 : (da3r[0] + (da3r[0] % tickGap == 0 ? 0 : tickGap - da3r[0] % tickGap)) * lrgLenMult, (da3r[da3r.length - 1] + tickGap - da3r[da3r.length - 1] % tickGap) * lrgLenMult, tickGap * lrgLenMult);
        distTicks = distTicks.filter(function(d) {
            return segment.distanceArr2Copy ? d >= (segment.distanceArr2Copy.last() - segment.distanceArr2Copy[filtIndExtent[1]]) * lrgLenMult : true;
        });
    }
    distTicks = distTicks.map(function(d) {
        return d.round(4)
    });
    if (isInit && parseInt(d3.select('#svgWrapper svg').style('width')) / 50 < distTicks.length) {
        distTicks = distTicks.filter(function(d, i) {
            return i % 2 == 0
        });
    }
    var curTick = 0;
    var sd = type == 0 ? segment.distanceArr2[0] : 0;
    var sd2 = 0;
    distIds = [];
    if (type == 0 || type == 2) {
        for (var i = 0; i < segment.xyz2.length; i++) {
            var d = segment.xyz2[i].d + sd;
            while (curTick < distTicks.length && d * lrgLenMult >= distTicks[curTick]) {
                distIds.push({
                    i: i,
                    dist: distTicks[curTick]
                });
                curTick++;
            }
        }
    } else {
        sd = (type == 1 ? segment.distance2 : segment.da3[segment.da3.length - 1]);
        sd2 = segment.da3[0];
        for (var i = segment.xyz2.length - 1; i >= 0; i--) {
            var d = segment.xyz2[i].d;
            while (curTick < distTicks.length && (sd - (d + sd2)) * lrgLenMult >= distTicks[curTick]) {
                distIds.push({
                    i: i + ((sd - (d + sd2)) * lrgLenMult == distTicks[curTick] ? 0 : 1),
                    dist: distTicks[curTick]
                });
                curTick++;
            }
        }
    }

    /*  .forEach(function(d,i){
      if (typeof(distIds[Math.floor((d - segment.distanceArr2[0])*lrgLenMult)])==='undefined') {
        distIds[Math.floor((d - segment.distanceArr2[0])*lrgLenMult)] = i;
      }
    });*/

    distIds
        /*.filter(function(d, i) {
                return type == 2 || d.i > 0
            })*/
        .forEach(function(d, i) {
            var ll;
            var dm = (type == 0 || type == 2 ? 1 : -1);
            if ((sd + dm * segment.xyz2[d.i].d - sd2) * lrgLenMult == d.dist) {
                x0 = segment.xyz2[d.i].x;
                y0 = segment.xyz2[d.i].y;
                z0 = segment.xyz2[d.i].z;
                ll = segment.xyz2[d.i].obj;
            } else {
                var m = (d.dist - (sd + dm * segment.xyz2[d.i - 1].d - sd2) * lrgLenMult) / ((sd + dm * segment.xyz2[d.i].d - sd2) * lrgLenMult - (sd + dm * segment.xyz2[d.i - 1].d - sd2) * lrgLenMult);
                x0 = segment.xyz2[d.i - 1].x + m * (segment.xyz2[d.i].x - segment.xyz2[d.i - 1].x);
                y0 = segment.xyz2[d.i - 1].y + m * (segment.xyz2[d.i].y - segment.xyz2[d.i - 1].y);
                z0 = segment.xyz2[d.i - 1].z + m * (segment.xyz2[d.i].z - segment.xyz2[d.i - 1].z);
                ll = {
                    lat: segment.xyz2[d.i - 1].obj.lat + m * (segment.xyz2[d.i].obj.lat - segment.xyz2[d.i - 1].obj.lat),
                    lng: segment.xyz2[d.i - 1].obj.lng + m * (segment.xyz2[d.i].obj.lng - segment.xyz2[d.i - 1].obj.lng)
                }
            }
            z1 = z0 + Math.min((zmax - zmin) / 2, Math.min(200, 0.01 * (segment.da3[segment.da3.length - 1] - segment.da3[0])));
            //zmax + (zmax-zmin) * 0.1;
            //z0 + Math.min(200, 0.01 *(segment.distanceArr2[segment.distanceArr2.length-1] - segment.distanceArr2[0]));
            //Math.max(Math.abs((xmax - xmin)), Math.abs((ymax - ymin)))*0.01; //(zmax-zmin)*0.5;
            z0 = (z0 - zmin);
            z0 *= altMult;
            //z1 = (z1 - zmin);
            //z1 *= altMult * (is3d ? 1 : 5);
            z1 = z0 + stickH;
            if (!isInit) {
                var id = zPoly.length;
                zPoly[id] = new Poly3D('main part distMarker dm' + d.dist.toString().replace('.', '_'), S, gridCol, gridCol, gridCol, 1, false, false);
                zPoly[id].AddPoint(x0, y0, z0);
                zPoly[id].AddPoint(x0, y0, z1);
                zPoly[id].Update();
            } else {
                p3dDistArr.push(Math.round(x0 * 100) / 100);
            }
        });
}

function mouseZoom(amount) {
    S.ZoomAll += amount / (is3d ? 1000 / S.ZoomAll : 5000 / S.ZoomAll);
    S.ZoomAll = Math.max(0.001, Math.min(2, S.ZoomAll));
    zoomAll = S.ZoomAll;

    if (moveTimer == null) {
        moveTimer = setTimeout(function() {
            var startTime = new Date().getTime();
            S.Sort();
            S.Draw();
            setDistElevMarkers();
            p3dSetDistScale();
            drawTime = new Date().getTime() - startTime;
            moveTimer = null;
        }, Math.min(drawTime * 1.1, 250));
    }
}

function handleMove3d(obj, mouseTouch, type) {
    d3.selectAll('#profile3dsvgContainer svg .Scene path.noSize').style('display', null);
    window.clearInterval(aniInt);
    window.clearTimeout(aniTimer);

    //drawTime

    /*if (!locked) {
        d3.select('#getImageBtn')
          .html('Image and stored view out of date.<br/>Click previous button to get new image.')
          .attr('disabled', 'disabled');
      }*/

    if (type == 'touch' && mouseTouch.length > 1) {
        var l_dragX = mouseTouch[0][0];
        var l_dragY = mouseTouch[0][1];

        l_dragX = 360 * l_dragX / parseInt(d3.select('#profile3dsvgContainer').style('width'));
        l_dragY = (90 * l_dragY / parseInt(d3.select('#profile3dsvgContainer').style('height'))) - 90;
        if (mouseTouch.length == 2) {
            l_zoomX1 = mouseTouch[0][0];
            l_zoomY1 = mouseTouch[0][1];
            l_zoomX2 = mouseTouch[1][0];
            l_zoomY2 = mouseTouch[1][1];

            if (mouseTouch.length == 2 && zoomX1 == null) {
                zoomX1 = l_zoomX1;
                zoomY1 = l_zoomY1;
                zoomX2 = l_zoomX2;
                zoomY2 = l_zoomY2;
            }
        }

        if (dragX != null) {
            var xChange = dragX - l_dragX;
            var yChange = dragY - l_dragY;

            if (zoomX1 != null && mouseTouch.length == 2) {
                S.ZoomAll += (hypot(l_zoomX1 - l_zoomX2, l_zoomY1 - l_zoomY2) - hypot(zoomX1 - zoomX2, zoomY1 - zoomY2)) / (is3d ? 1000 / S.ZoomAll : 5000 / S.ZoomAll);
                S.ZoomAll = Math.max(0.001, Math.min(2, S.ZoomAll));
                zoomAll = S.ZoomAll;

                zoomX1 = l_zoomX1;
                zoomY1 = l_zoomY1;
                zoomX2 = l_zoomX2;
                zoomY2 = l_zoomY2;
            }

            /*if (mouseTouch.length == 2) {
                S.Center.x += xChange * 10 * (Math.sin(spinXVal * Math.PI / 180)) + yChange * 10 * (Math.cos(spinXVal * Math.PI / 180));
                S.Center.y += yChange * 10 * (Math.sin((spinXVal - 180) * Math.PI / 180)) + xChange * 10 * (Math.cos(spinXVal * Math.PI / 180));
              } else {*/
            S.XM -= xChange * 3;
            S.YM -= yChange * 6;

            if (is3d) {
                xm3d = S.XM;
                ym3d = S.YM;
                zoomAll3d = S.ZoomAll;
            } else {
                xm2d = S.XM;
                ym2d = S.YM;
                zoomAll2d = S.ZoomAll;
            }
            //}
            if (moveTimer == null) {
                moveTimer = setTimeout(function() {
                    var startTime = new Date().getTime();
                    S.Sort();
                    S.Draw();
                    setDistElevMarkers();
                    p3dSetDistScale();
                    drawTime = new Date().getTime() - startTime;
                    moveTimer = null;
                }, Math.min(drawTime * 1.1, 250));
            }
        }

        dragX = l_dragX;
        dragY = l_dragY;

    } else {
        if (type == 'touch') {
            mouseTouch = mouseTouch[0];
        }

        if (d3.event.ctrlKey || d3.event.altKey) {
            var l_dragX = mouseTouch[0];
            var l_dragY = mouseTouch[1];

            l_dragX = 360 * l_dragX / parseInt(d3.select('#profile3dsvgContainer').style('width'));
            l_dragY = (90 * l_dragY / parseInt(d3.select('#profile3dsvgContainer').style('height'))) - 90;

            if (dragX != null) {
                var xChange = dragX - l_dragX;
                var yChange = dragY - l_dragY;

                S.XM -= xChange * 3;
                S.YM -= yChange * 6;

                if (is3d) {
                    xm3d = S.XM;
                    ym3d = S.YM;
                } else {
                    xm2d = S.XM;
                    ym2d = S.YM;
                }
                //}
                if (moveTimer == null) {
                    moveTimer = setTimeout(function() {
                        var startTime = new Date().getTime();
                        S.Sort();
                        S.Draw();
                        setDistElevMarkers();
                        p3dSetDistScale();
                        drawTime = new Date().getTime() - startTime;
                        moveTimer = null;
                    }, Math.min(drawTime * 1.1, 250));
                }
            }

            dragX = l_dragX;
            dragY = l_dragY;
        } else {

            var l_dragX = mouseTouch[0];
            var l_dragY = mouseTouch[1];

            l_dragX = 360 * l_dragX / parseInt(d3.select('#profile3dsvgContainer').style('width'));
            l_dragY = (90 * l_dragY / parseInt(d3.select('#profile3dsvgContainer').style('height'))) - 90;

            if (dragX != null && spinYVal + dragY - l_dragY <= 0) {
                var xChange = dragX - l_dragX;
                var yChange = dragY - l_dragY;

                yChange = spinYVal + yChange > 0 ? 0 : (spinYVal + yChange < -90 ? 0 : yChange);

                spinXVal -= xChange;
                spinYVal += yChange;
                S.ChangeViewer(yChange, -xChange);
                spinLightXVal -= xChange;
                spinLightYVal += yChange;
                S.ChangeLight(yChange, -xChange);

                if (moveTimer == null) {
                    var startTime = new Date().getTime();
                    S.Sort();
                    S.Draw();
                    setDistElevMarkers();
                    p3dSetDistScale();

                    if (resizeViewTimer != null) {
                        clearTimeout(resizeViewTimer);
                    }
                    if (location.hash == '') {
                        resizeViewTimer = setTimeout(resizeView, 500);
                    }

                    drawTime = new Date().getTime() - startTime;
                    moveTimer = setTimeout(function() {
                        moveTimer = null;
                    }, Math.min(drawTime * 1.1, 250));

                    hideImage(false);
                }
            }
        }
    }

    if (is3d) {
        spinXVal3d = spinXVal,
            spinYVal3d = spinYVal,
            spinLightXVal3d = spinLightXVal,
            spinLightYVal3d = spinLightYVal;
    }

    dragX = l_dragX;
    dragY = l_dragY;
    //console.debug('spinXVal: ' + spinXVal + ', spinYVal: ' + spinYVal + ', spinLightXVal: ' + spinLightXVal + ', spinLightYVal: ' + spinLightYVal);

    if (!spinning) {
        aniTimer = window.setTimeout(animateRoute, 2000);
    }
}

function resizeView() {
    resizeViewTimer = null;
    svgM.bottom = 20 + p3dSizeMult * 30 * (is3d ? 0.8 : 1) * (isCreatingImage ? 2 : 1);
    svgM.top = 44 + (isCreatingImage ? p3dSizeMult * 30 : 1)
    d3.selectAll('#profile3dsvgContainer svg .Scene path:not(.main)').style('display', 'none');
    /*d3.selectAll('#profile3dsvgContainer svg .Scene path')[0]
        .filter(function(d) {
          var t = d3.select(d).attr('d').replace('M ', '').replace(' z', '').split(' L ');
          return t.length == 2 && t[0] == t[1]
        })
        .forEach(function(d) {
          d3.select(d).classed('noSize', true).style('display', 'none');
        });*/
    var svgRect = d3.select('#profile3dsvgContainer  #svgWrapper').node().getBoundingClientRect();
    var sceneRect = d3.select('#profile3dsvgContainer svg .Scene').node().getBoundingClientRect();
    S.ZoomAll *= Math.min((svgRect.width - (svgM.left + svgM.right)) / sceneRect.width, (svgRect.height - (svgM.top + svgM.bottom)) / sceneRect.height);
    //    d3.selectAll('#profile3dsvgContainer svg .Scene path:not(.noSize)').style('display', null);
    S.Draw();
    /*d3.selectAll('#profile3dsvgContainer svg .Scene path')[0]
        .filter(function(d) {
          var t = d3.select(d).attr('d').replace('M ', '').replace(' z', '').split(' L ');
          return t.length == 2 && t[0] == t[1]
        })
        .forEach(function(d) {
          d3.select(d).classed('noSize', true).style('display', 'none');
        });*/
    d3.select('#profile3dsvgContainer svg .Scene').attr('transform', null);
    var svgRect = d3.select('#profile3dsvgContainer  #svgWrapper').node().getBoundingClientRect();
    var sceneRect = d3.select('#profile3dsvgContainer svg .Scene').node().getBoundingClientRect();
    d3.select('#profile3dsvgContainer svg .Scene').attr('transform', 'translate(' +
        (svgM.left + (((svgRect.left - svgM.left - sceneRect.left) + (svgRect.right - svgM.right - sceneRect.right)) / 2)) + ', ' +
        (svgM.top + (((svgRect.top - svgM.top - sceneRect.top) + (svgRect.bottom - svgM.bottom - sceneRect.bottom)) / 2)) + ')')
    d3.selectAll('#profile3dsvgContainer svg .Scene path:not(.main)').style('display', null);
    //d3.selectAll('#profile3dsvgContainer svg .Scene path:not(.noSize)').style('display', null);
    setDistElevMarkers();
    p3dSetDistScale();
}

function set3dSegment(smoothing, simplifyMult, start, end) {
    if (typeof(segment.latLngArr2) === 'undefined' || segment.latLngArr2.length == 0) {
        return;
    }
    var localDetailLevel = detailLevel;
    if (!is3d) {
        localDetailLevel = detailLevel / 4;
    }

    if (location.hash != '') {
        var hashArr = location.hash.substr(1).split('|');
        if (hashArr[0] != '') {
            start = parseInt(hashArr[0]);
            end = parseInt(hashArr[1]);
        }
        if (hashArr.length > 2) {
            p3dBG = '#' + hashArr[2];
        }
        if (hashArr.length > 3) {
            p3dFG = '#' + hashArr[3];
        }
    }
    if (typeof(fromIndex) !== 'undefined') {
        start = fromIndex;
    }
    if (typeof(toIndex) !== 'undefined') {
        end = toIndex;
    }
    p3dStart = start;
    p3dEnd = end;
    p3dBGr = hexToRgb(p3dBG).r;
    p3dBGg = hexToRgb(p3dBG).g;
    p3dBGb = hexToRgb(p3dBG).b;

    if (origSpinXVal == -10000) {
        origSpinXVal = spinXVal;
    }
    if (is3d) {
        if (origElevMult3D == -10000) {
            origElevMult3D = elevMult;
        }
    } else {
        if (origElevMult2D == -10000) {
            origElevMult2D = elevMult;
        }
    }

    if (smoothing == null) {
        smoothing = 10;
    }
    if (simplifyMult == null) {
        simplifyMult = lastSimplifyMult;
    } else {
        lastSimplifyMult = simplifyMult;
    }
    segment.xyz = [];

    var lla2 = segment.latLngArr2;
    var ea2 = segment.elevationArr2;
    segment.da3 = segment.distanceArr2;

    if (start != null && !isNaN(start)) {
        lla2 = segment.latLngArr2.slice(start, end);
        ea2 = segment.elevationArr2.slice(start, end);
        ea2 = segment.elevationArr2.slice(start, end);
        segment.da3 = segment.distanceArr2.slice(start, end);
    } else {
        p3dStart = 0;
        p3dEnd = segment.elevationArr2.length - 1;
    }

    xmin = d3.min(lla2.map(function(e) {
        return (e.lng == 0 ? 1000 : e.lng);
    }));
    xmax = d3.max(lla2.map(function(e) {
        return (e.lng == 0 ? -1000 : e.lng);
    }));
    ymin = d3.min(lla2.map(function(e) {
        return (e.lat == 0 ? 1000 : e.lat);
    }));
    ymax = d3.max(lla2.map(function(e) {
        return (e.lat == 0 ? -1000 : e.lat);
    }));
    zmin = d3.min(ea2.map(function(e) {
        return e;
    }));
    zmax = d3.max(ea2.map(function(e) {
        return e;
    }));
    if (zmin == zmax) {
        zmin--;
    }

    if (show3dPlaceMarkers && (typeof(placeMarkers) === 'undefined' || minLng != xmin || maxLng != xmax || minLat != ymin || maxLat != ymax)) {
        window.clearTimeout(placeMarkerTimer);
        placeMarkerTimer = window.setTimeout(function() {
            getGeoNames(maxLat, minLat, maxLng, minLng)
        }, 500);
    }
    minLng = xmin;
    maxLng = xmax;
    minLat = ymin;
    maxLat = ymax;

    /*if (xmax-xmin > ymax-ymin)
      {
        ymin = ymin - ((ymax - ymin) - (xmax - xmin))/2;
        ymax = ymax + ((ymax - ymin) - (xmax - xmin))/2;
      }
      else
      {
        xmin = xmin - ((xmax - xmin) - (ymax - ymin))/2;
        xmax = xmin + ((xmax - xmin) - (ymax - ymin))/2;
      }*/

    maxDist = distLatLon(ymin, xmin, ymax, xmax);
    smoothArr = [];

    for (var i = 0; i < lla2.length; i++) {
        if (lla2[i].lng != 0 && lla2[i].lat != 0)
            segment.xyz.push({
                /*x: (segment.latLngArr2[i].lng+xmin)/(xmax-xmin),
                  y: (segment.latLngArr2[i].lat+ymin)/(ymax-ymin), */
                x: (is3d ? distLatLon(lla2[i].lat, xmin, lla2[i].lat, lla2[i].lng) : segment.da3[i] - segment.da3[0]),
                y: (is3d ? distLatLon(ymin, lla2[i].lng, lla2[i].lat, lla2[i].lng) : 0),
                z: (ea2[i] - zmin) / (zmax - zmin),
                //z: rollingAve((ea2[i] - zmin) / (zmax - zmin), smoothing),
                d: segment.da3[i] - segment.da3[0],
                obj: lla2[i]
            });
    }

    if (typeof(elevationPreSmoothed) === 'undefined') {
        var smoothed = filterData(segment.xyz.map(function(d) {
            return {
                x: d.d,
                y: d.z
            }
        }), (typeof(segment.corrected) !== 'undefined' && segment.corrected ? 20 : 30));
        segment.xyz.forEach(function(d, i) {
            d.z = smoothed[i].ySmooth;
        });
        segment.elevationArr2 = segment.xyz.map(function(d){return d.z * (zmax-zmin) + zmin});
        window.elevationPreSmoothed = true;
    }

    totalDist = segment.distanceArr2.last() - segment.distanceArr2[0];
    if (!is3d) {
        /*var lastX, lastY;
          for (var i = 0; i < segment.xyz.length; i++) {
            var curX = segment.xyz[i].x;
            var curY = segment.xyz[i].y;
            if (i == 0) {
              segment.xyz[i].x = 0;
              segment.xyz[i].y = 0;
            } else {
              segment.xyz[i].x = segment.xyz[i - 1].x + Math.sqrt((curX - lastX) * (curX - lastX) + (curY - lastY) * (curY - lastY));
              segment.xyz[i].y = 0;
            }

            lastX = curX;
            lastY = curY;
          }*/
        //totalDist = segment.xyz[segment.xyz.length - 1].x;
        if (localDetailLevel >= detailCutOff) {
            segment.xyz2 = [];
            segment.xyz.forEach(function(d, i) {
                if (segment.xyz2.length == 0 || d.d >= segment.xyz2[segment.xyz2.length - 1].d + (detailSize / lrgLenMult) || i == segment.xyz.length - 1) {
                    segment.xyz2.push(d);
                }
            });
        } else {
            segment.xyz2 = simplify(segment.xyz, localDetailLevel / ((['activity', 'route', 'routes'].indexOf(segment.type) == -1 ? 2 : 0.5) * 30), false); //0.02
        }
        if (segment.xyz2.length < 4) {
            segment.xyz2 = simplify(segment.xyz, 2 * localDetailLevel / ((['activity', 'route', 'routes'].indexOf(segment.type) == -1 ? 2 : 0.5) * 30), false); //0.02
            if (segment.xyz2.length < 4) {
                segment.xyz2 = segment.xyz;
            }
        }
        var i = 1;
        var lastLen = -1;
        /*while (lastLen != segment.xyz2.length && segment.xyz2.length > Math.min(100, segment.elevationArr2.length / 4)) {
            lastLen = segment.xyz2.length;
            segment.xyz2 = simplify(segment.xyz, i++ * 0.001, true);
          }*/
    } else {
        /*totalDist = 0;
        for (var i = 1; i < segment.xyz.length; i++) {
            var xdif = segment.xyz[i].x - segment.xyz[i - 1].x;
            var ydif = segment.xyz[i].y - segment.xyz[i - 1].y;
            totalDist += Math.sqrt((xdif * xdif) + (ydif * ydif));
        }*/

        //segment.elevSimp = simplify(segment.xyz.map(function(d,i){return {x:segment.da3[i] - segment.da3[0], y:0, z:d.z, d:d.d}}), localDetailLevel / 30, false); //0.02

        if (localDetailLevel >= detailCutOff) {
            segment.xyz2 = [];
            segment.xyz.forEach(function(d, i) {
                if (segment.xyz2.length == 0 || d.d >= segment.xyz2[segment.xyz2.length - 1].d + (detailSize / lrgLenMult) || i == segment.xyz.length - 1) {
                    segment.xyz2.push(d);
                }
            });
        } else {
            segment.xyz2 = simplify(segment.xyz, simplifyMult * localDetailLevel * totalDist / (1 / lrgLenMult), true);
        }
    }

    aniTime = Math.max(3000, totalDist / 20);

    for (var i = 0; i < segment.xyz.length; i++) {
        segment.xyz[i].x = segment.xyz[i].x;
        segment.xyz[i].y = segment.xyz[i].y;
        segment.xyz[i].z = segment.xyz[i].z * (zmax - zmin) + zmin;
    }

    xmin = d3.min(segment.xyz.map(function(e) {
        return e.x;
    }));
    xmax = d3.max(segment.xyz.map(function(e) {
        return e.x;
    }));
    ymin = d3.min(segment.xyz.map(function(e) {
        return e.y;
    }));
    ymax = d3.max(segment.xyz.map(function(e) {
        return e.y;
    }));

    d3.selectAll('#profile3dsvg .Scene *').remove();
    if (!isCreatingImage) {
        sizep3d();
    }

    p3dInit();

    if (p3dDefault2d) {
        p3d2dbtn.node().click();
        p3dDefault2d = false;
    }
}

var smoothArr = [];

function rollingAve(val, items) {
    smoothArr.push(val);
    if (smoothArr.length > items) {
        smoothArr.shift();
    }
    return d3.mean(smoothArr);
}

function sizep3d() {
    //$('#profile3dsvgContainer').height(Math.max(350, window.innerHeight - (typeof(activity) === 'undefined' ? 250 : 382)));
    $('#profile3dsvgContainer').height(Math.min($('#profile3dsvgContainer').width() * 2 / 3, window.innerHeight - (typeof(activity) === 'undefined' && typeof(route) === 'undefined' ? 250 : 327)));
    $('#profile3dsvg').height($('#profile3dsvgContainer').height());
    $('#profile3dsvg').width($('#profile3dsvgContainer').width());
    $('#profileTSsvg').height($('#profile3dsvgContainer').height());
    $('#profileTSsvg').width($('#profile3dsvgContainer').width());
    //$('#profile3dCanvas2').height($('#profile3dsvgContainer').height());
    //$('#profile3dCanvas2').width($('#profile3dsvgContainer').width());
    document.getElementById('profile3dCanvas2').height = $('#profile3dsvgContainer').height();
    document.getElementById('profile3dCanvas2').width = $('#profile3dsvgContainer').width();
}

var spinTimer;
var spinning = false,
    spinAmount = 1,
    markerFontSize = 10;

function p3dSetSpin(s) {
    p3dSetupSpin()
    spinAmount = s;
    if (!spinning) {
        spinning = true;
        window.clearInterval(aniInt);
        window.clearTimeout(aniTimer);
        spin();
    }
}

var showingPart = 0;

function p3dSetupSpin() {
    window.clearInterval(aniInt);
    window.clearTimeout(aniTimer);
    if (d3.select('#profile3dsvgContainer').style('width') != '1280px') {
        d3.select('#profile3dsvgContainer').style('width', '1280px').style('height', '720px');
        d3.select('#mapChartContainer3').style('display', 'none');
        set3dSegment();
    }
    d3.selectAll('.keyG, #optionsWrapper').style('display', 'none');
    d3.select('.pbsG').attr('transform', 'translate(2,720) scale(2)');
    d3.selectAll('.distMarkerText').style('font-size', '20px');
    d3.select('#profile3dsvg').append('text')
        .attr({
            x: 10,
            y: 40,
            style: 'font-size:40px'
        })
        .text(segment.name);
    markerFontSize = 20;
}

function p3dShowProfile(speed, spin, fadeOpacity, fadeDuration) {
    p3dSetupSpin()
    showingPart = 0
    d3.selectAll('.Scene .main, .Scene .shadow, .distMarkersG').style('display', 'none').style('opacity', '1');
    setTimeout(function() {
        var showingPartInt = window.setInterval(function() {
            if (showingPart >= segment.xyz2.length - 1) {
                d3.selectAll('.Scene .main, .Scene .shadow, .distMarkersG').style('display', null);
                clearInterval(showingPartInt);
                p3dSetSpin(spin);
            } else {
                //d3.selectAll('.Scene .main, .Scene .shadow').style('display', 'none');
                if (fadeOpacity) {
                    d3.selectAll('.Scene .part' + showingPart).style('display', null).transition().duration(fadeDuration ? fadeDuration : 250).style('opacity', fadeOpacity);
                } else {
                    d3.selectAll('.Scene .part' + showingPart).style('display', null);
                }
                for (var i = 0; i < showingPart - 7; i++) {
                    if (segment.xyz2[i].x > segment.xyz2[showingPart].x - 40 && segment.xyz2[i].x < segment.xyz2[showingPart].x + 40 && segment.xyz2[i].y > segment.xyz2[showingPart].y - 40 && segment.xyz2[i].y < segment.xyz2[showingPart].y + 40) {
                        d3.selectAll('.Scene .part' + i).style('display', 'none');
                    }
                }
                showingPart++;
            }
        }, speed);
    }, 250);
}

function spin() {
    if (!spinning) return;
    var startTime = new Date().getTime();
    spinXVal += spinAmount;
    spinLightXVal += spinAmount;
    S.ChangeViewer(0, spinAmount);
    S.ChangeLight(0, spinAmount);
    S.Sort();
    S.Draw();
    setDistElevMarkers();
    p3dSetDistScale();
    drawTime = new Date().getTime() - startTime;
    spinTimer = window.setTimeout(spin, Math.max(10, Math.min(drawTime * 1.2, 250)));
}

var viewBG = d3.select('#optionsWrapper').append('div')
    .classed('btn-group', true)
    .style('margin-left', '5px')
    .attr('data-toggle', 'buttons-radio')
    .attr('id', 'viewBG');

var p3d3dbtn = viewBG.append('button')
    .attr('type', 'button')
    .attr('data-val', '3D')
    .classed('btn btn-primary btn-mini active', true)
    .text('3D')
    .on('click', function() {
        d3.selectAll('#profile3dsvg').style('display', null);
        d3.selectAll('#profileTSsvg').style('display', 'none');
        d3.selectAll('#optionsWrapper > button').style('display', null);

        if (typeof(embed) !== 'undefined' && embed) {
            show3dGradMarkers = false;
        }
        is3d = true;
        isTS = false;

        if (document.getElementById('tsOptions') != null) {
            document.getElementById('tsOptions').style.display = 'none';
        }
        if (document.getElementById('p3dReverse2DXCB') != null) {
            document.getElementById('p3dReverse2DXCB').parentElement.style.display = 'none';
            document.getElementById('showGridCB').parentElement.style.display = null;
            if (document.getElementById('transparencyCB') != null) {
              document.getElementById('transparencyCB').parentElement.style.display = null;
            }
        }

        spinXVal = spinXVal3d;
        spinYVal = spinYVal3d;
        spinLightXVal = spinLightXVal3d;
        spinLightYVal = spinLightYVal3d;
        zoom = zoom3d,
            elevMult = elevMult3d;
        xm = xm3d;
        ym = ym3d;
        zoomAll = zoomAll3d;
        d3.select('#elevRange').attr('value', elevMult);
        if (document.getElementById('elevRange') != null) {
            document.getElementById('elevRange').value = elevMult;
        }
        set3dSegment();
    });

var p3dDefault2d = getParameterByName('default2d').toLowerCase() == 'y';
d3.select('#embedModal .form-horizontal').append('div').classed('control-group', true).style('margin-bottom', '0px')
    .append('div').classed('controls', true).append('label').classed('checkbox', true).html('<input type="checkbox" id="defaultTo2Dcb"> Default to 2D');
d3.select('#embedModal .form-horizontal').append('div').classed('control-group', true)
    .append('div').classed('controls', true).append('label').classed('checkbox', true).html('<input type="checkbox" id="defaultToMilescb"> Use miles');
d3.select('#defaultTo2Dcb').on('change', function() {
    setEmbed();
});
d3.select('#defaultToMilescb').on('change', function() {
    setEmbed();
});

var p3d2dbtn = viewBG.append('button')
    .attr('type', 'button')
    .attr('data-val', '2D')
    .classed('btn btn-primary btn-mini', true)
    .text('2D')
    .on('click', function() {
        d3.selectAll('#profile3dsvg').style('display', null);
        d3.selectAll('#profileTSsvg').style('display', 'none');
        d3.selectAll('#optionsWrapper > button').style('display', null);
        if (typeof(embed) !== 'undefined' && embed) {
            show3dGradMarkers = true;
        }
        var zExt = d3.extent(segment.xyz.map(function(e) {
            return e.z;
        }));

        is3d = false;
        isTS = false;

        if (document.getElementById('tsOptions') != null) {
            document.getElementById('tsOptions').style.display = 'none';
        }
        if (document.getElementById('p3dReverse2DXCB') != null) {
            document.getElementById('p3dReverse2DXCB').parentElement.style.display = null;
            document.getElementById('showGridCB').parentElement.style.display = 'none';
            if (document.getElementById('transparencyCB') != null) {
              document.getElementById('transparencyCB').parentElement.style.display = null;
            }
        }

        if (typeof(route) !== 'undefined') {
            spinXVal = 90;
            spinYVal = -10;
        } else {
            spinXVal = 116;
            spinYVal = -16;
        }
        spinLightXVal = spinXVal - 30; //86,
        spinLightYVal = spinYVal - 5;//-21,
        zoom = zoom2d;
        xm = xm2d;
        ym = ym2d;
        zoomAll = zoomAll2d;
        elevMult = (elevMult2d == null ? Math.min(5, totalDist / (4 * (zExt[1] - zExt[0]))) : elevMult2d);
        d3.select('#elevRange').attr('value', elevMult);
        if (document.getElementById('elevRange') != null) {
            document.getElementById('elevRange').value = elevMult;
        }

        set3dSegment();
    });

d3.select('#optionsWrapper').append('button')
    .classed('btn btn-primary btn-mini', true)
    .attr('data-toggle', 'button')
    .text('Spin')
    .on('click', function() {
        if (spinning) {
            spinning = false;
            aniTimer = window.setTimeout(animateRoute, 1000);
        } else {
            spinning = true;
            window.clearInterval(aniInt);
            window.clearTimeout(aniTimer);
            spin();
        }
    });

function getImageClick() {
    $('#profileImgModal').modal('hide');
    $('#pleaseWaitDialog').modal();
    window.setTimeout(function() {
        d3.select('#profileImg').attr('src', null);
        $('#pleaseWaitDialog').modal('hide');
        $('#profileImgModal').modal();
        isCreatingImage = true;
        displayImage();
    }, 100);
}

if (typeof(embed) === 'undefined') {

    /*<div class="dropdown">
      <a class="dropdown-toggle" id="drop4" role="button" data-toggle="dropdown" href="#">Dropdown <b class="caret"></b></a>
      <ul id="menu1" class="dropdown-menu" role="menu" aria-labelledby="drop4">
        <li role="presentation"><a role="menuitem" tabindex="-1" href="#">Action</a></li>
        <li role="presentation"><a role="menuitem" tabindex="-1" href="#">Another action</a></li>
        <li role="presentation"><a role="menuitem" tabindex="-1" href="#">Something else here</a></li>
        <li role="presentation" class="divider"></li>
        <li role="presentation"><a role="menuitem" tabindex="-1" href="#">Separated link</a></li>
      </ul>
    </li>*/

    d3.select('#optionsWrapper').append('button')
        .style('margin-left', '5px')
        .classed('btn btn-primary btn-mini', true)
        .text('Get image')
        .on('click', getImageClick);

    var imgSizes = [{
        desc: 'Standard (1024x512px)',
        x: 1024,
        y: 512
    }, {
        desc: 'Huge (1920x1080px)',
        x: 1920,
        y: 1080
    }, {
        desc: 'Square (640x640px)',
        x: 640,
        y: 640
    }, {
        desc: 'Big square (1080x1080px)',
        x: 1080,
        y: 1080
    }, {
        desc: 'Cinema (1080x360px)',
        x: 1080,
        y: 360
    }, {
        desc: 'Big cinema (1920x720px)',
        x: 1920,
        y: 720
    }, {
        desc: 'Massive (3300x3300px)',
        x: 3300,
        y: 3300
    }, {
        desc: '4K (3840x2160px)',
        x: 3840,
        y: 2160
    }, {
        desc: 'Eurosport (1024x750px)',
        x: 1024,
        y: 750
    }, {
        desc: 'Instagram Story (1080x1920px)',
        x: 1080,
        y: 1920
    }, {
        desc: 'Zwift (3840x2160px)',
        x: 3840,
        y: 2160
    }];

    vvt = d3.select('#imgSizeSelect')
        .on('change', function() {
            imageSize = this.selectedOptions[0].__data__;
        })
        .selectAll('options')
        .data(imgSizes)
        .enter()
        .append('option')
        .text(function(d) {
            return d.desc
        });

    d3.select('#swapImgSize')
        .on('click', getImageClick);
}

function setEmbed() {
    //var embedStyle = document.querySelector('input[name="embedBGRadios"]:checked').value;
    //var embedMarker = document.querySelector('input[name="embedMarkersRadios"]:checked').value;
    var embedMap = document.querySelector('input[name="embedMapRadios"]:checked').value;
    var default2d = document.getElementById('defaultTo2Dcb') != null && document.getElementById('defaultTo2Dcb').checked ? '?default2d=y' : '';
    default2d += document.getElementById('defaultToMilescb') != null && document.getElementById('defaultToMilescb').checked ? (default2d == '' ? '?' : '&') + 'units=i' : '';

    var si = ((segment.type == 'activity' || segment.type == 'route') ? (typeof(id) === 'undefined' ? 0 : id) : (typeof(activity) === 'undefined' ? (typeof(segmentId) === 'undefined' ? id : segmentId) : activity.id));

    if (pageType3d == 'activity') {
        d3.select('#embedTA').text('<iframe style="width:100%;height:' + (embedMap == '2' ? '600' : '450') + 'px;" src="https://veloviewer.com/athletes/' + contextAthleteId + '/activities/' + si + '/embed' + embedMap + '?name=' + encodeURIComponent(activity.name) + default2d.replace('?', '&') + '" frameborder="0" scrolling="no"></iframe>');
    } else {
        d3.select('#embedTA').text('<iframe style="width:100%;height:' + (embedMap == '2' ? '600' : '450') + 'px;" src="https://veloviewer.com/' + (segment.type == 'activity' || segment.type == 'routes' || segment.type == 'route' ? segment.type : 'segments') + '/' + si + '/embed' + embedMap + default2d + '" frameborder="0" scrolling="no"></iframe>');
    }
}

d3.selectAll('.embedBGRadios, .embedMapRadios, .embedMarkersRadios').on('change', setEmbed);

if (typeof(embed) === 'undefined' && typeof(pageType3d) !== 'undefined' /*&& pageType3d != 'activity'*/ ) {
    d3.select('#optionsWrapper').append('button')
        .classed('btn btn-primary btn-mini', true)
        .text('Embed')
        .attr('title', 'Get the code to embed the current view of the profile on your own website.')
        .on('click', function() {
            d3.select('#profileImg').attr('src', null);
            $('#embedModal').modal();
            setEmbed();
            if (origSpinXVal != spinXVal || (is3d ? origElevMult3D != elevMult3d : origElevMult2D != elevMult2d)) {
                displayImage();
            }
        });
}

if (getParameterByName('hidePrint') == '') {
  d3.select('#optionsWrapper').append('button')
      .classed('btn btn-primary btn-mini', true)
      .html('3D Print')
      .attr('title', 'Print this profile at Print My Ride.')
      //.attr('style', (typeof(embed) === 'undefined' || !embed ? 'display:block;position:absolute;right:0px;top:21px' : ''))
      .on('click', function() {
          sendToPMR(segment.latLngArr2.map(function(d, i) { return { x: d.lng, y: d.lat, z: segment.elevationArr2[i] } }), (typeof(segment.altName) === 'undefined' || segment.altName == null || segment.altName == '' ? segment.name : segment.altName))
      });
}

/*if (typeof(locked) !== 'undefined' && !locked) {
  d3.select('#optionsWrapper').append('button')
    .classed('btn btn-primary btn-mini', true)
    .text('Set as default View')
    .attr('title', 'Sets the current view (angle and elevation stretch) to be the default view for all users.')
    .on('click', function() {
      $('#pleaseWaitDialog').modal();
      setTimeout(function() {
        displayImage();
      }, 100);
    });
}*/

//(origSpinXVal == 90 && origSpinXVal != spinXVal)

/*                          <button class="btn btn-mini btn-primary" id="saveThisView" style="display:none;margin:2px 0px 2px 0px;">Set default view</button>
                        <button class="btn btn-mini btn-primary" id="getImage" style="margin:2px 0px 2px 0px;">Get image</button>*/


var resizeTimer;
d3.select('#detailRange')
    .on('change', function(d) {
        detailLevel = Math.max(is3d ? 0.015 : 0.01, parseFloat(this.value));
        if (moveTimer == null) {
            moveTimer = setTimeout(function() {
                var startTime = new Date().getTime();
                if (isTS && typeof(p3dDrawTSProfile) !== 'undefined') {
                    p3dDrawTSProfile()
                } else {
                    set3dSegment();
                }
                drawTime = new Date().getTime() - startTime;
                moveTimer = null;
            }, Math.min(drawTime * 1.1, 250));
        }
    });
d3.select('#elevRange')
    .on('change', function(d) {
        elevMult = parseFloat(this.value);
        if (moveTimer == null) {
            moveTimer = setTimeout(function() {
                var startTime = new Date().getTime();
                if (is3d) {
                    elevMult3d = elevMult;
                } else {
                    if (isTS) {
                        elevMultTS = elevMult;
                    } else {
                        elevMult2d = elevMult;
                    }
                }
                d3.selectAll('#profile3dsvg .Scene *').remove();
                sizep3d();
                if (isTS && typeof(p3dDrawTSProfile) !== 'undefined') {
                    p3dDrawTSProfile()
                } else {
                    p3dInit();
                }
                drawTime = new Date().getTime() - startTime;
                moveTimer = null;
            }, Math.min(drawTime * 1.1, 250));
        }
    });

d3.select('#showGridCB')
    .on('change', function(d) {
        if (hasLocalStorage) {
            localStorage['grid3d'] = (document.getElementById('showGridCB') == null ? true : document.getElementById('showGridCB').checked);
        }
        d3.selectAll('#profile3dsvg .Scene *').remove();
        sizep3d();
        p3dInit();
    });

d3.select('#transparencyCB')
    .on('change', function(d) {
        if (document.getElementById('transparencyCB').checked) {
            d3.selectAll('.Scene path').attr('opacity', 0.667);
        } else {
            d3.selectAll('.Scene path').attr('opacity', null);
        }
    });

function filterData(points, tau) {
    tau = tau || 30; //30 seconds default
    var directions = [-1, 1];
    var yPrev, xPrev, ySmooth;

    var xds = points[3].x - points[0].x;
    var yds = points[3].y - points[0].y;
    var xde = points[points.length - 1].x - points[points.length - 3].x;
    var yde = points[points.length - 1].y - points[points.length - 3].y;

    points = d3.merge([d3.range(0, tau).map(function(d) { return { x: points[0].x + (d - tau) * xds, y: points[0].y + (d - tau) * yds } }), points, d3.range(0, tau).map(function(d) { return { x: points.last().x + d * xde, y: points.last().y + d * yde } })]);

    for (var a = 0; a < directions.length; a++) {
        var direction = directions[a];

        for (var i = 0; i < points.length; i++) {
            var n = direction == 1 ? i : points.length - 1 - i;

            var x = points[n].x;
            var y = points[n].y;

            if (ySmooth == null || xPrev == null || yPrev == null) {
                ySmooth = y
            } else {
                var u = Math.abs(x - xPrev) / tau;
                var z = Math.exp(-u);
                var dy = y - yPrev;

                if (u > 0) {
                    ySmooth = ySmooth * z + y * (1 - z) + (dy / u) * ((u + 1) * z - 1);
                }
            }

            //trace([y, ySmooth])
            yPrev = y;
            xPrev = x;

            if (direction == -1) {
                points[n].ySmooth = ySmooth;
            } else {
                points[n].ySmooth = (points[n].ySmooth + ySmooth) / 2;
            }
        }
    }
    return points.slice(tau, points.length - tau);
}

function resizeBy() {
    clearTimeout(resizeTimer);
    resizeTimer = setTimeout(function() {
        resize()
    }, 100);
}

function resize() {
    sizep3d();
    d3.selectAll('#profile3dsvg .Scene *').remove();
    p3dInit();
}

/*    if (typeof(defSpinXVal) !== 'undefined') {
      spinXVal = defSpinXVal;
      spinYVal = defSpinYVal;
      spinLightXVal = defSpinLightXVal;
      spinLightYVal = defSpinLightYVal;
      elevMult = defElevMult;
  }*/

//onload=set3dSegment(0);

function hexToRgb(hex) {
    var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
    if (result == null) {
        result = /^#?([a-f\d]{1})([a-f\d]{1})([a-f\d]{1})$/i.exec(hex);
        if (result != null) {
            result = result.map(function(d, i) {
                return (i == 0 ? d : d + d)
            });
        }
    }
    return result ? {
        r: parseInt(result[1], 16),
        g: parseInt(result[2], 16),
        b: parseInt(result[3], 16)
    } : null;
}

function getGeoNames(maxLat, minLat, maxLng, minLng) {
    $.ajax({
        url: '/api/getCity3D.php?maxLat=' + maxLat + '&minLat=' + minLat + '&maxLng=' + maxLng + '&minLng=' + minLng,
        success: function(result) {
            if (typeof(result.error) === 'undefined') {
                placeMarkers = result.geonames;
            } else {
                console.debug(result.error);
            }
            set3dSegment();
        }
    });
}

if (location.hash != '') {
    var hashArr = location.hash.split('|');
    if (hashArr[0] != '') {
        p3dStart = parseInt(hashArr[0].replace('#', ''));
        p3dEnd = parseInt(hashArr[1]);
    }
}

d3.select('#p3dDistElev')
    .style('margin-top', '-6px')
    .append('label')
    .style({
        'float': 'left'
    })
    .text('Markers:');
d3.select('#p3dDistElev')
    .append('select')
    .style({
        'max-width': '180px',
        'height': '20px',
        'margin-left': '4px'
    })
    .on('change', function(d) {
        show3dDistMarkers = false;
        show3dDistEndMarkers = false;
        show3dDistSelMarkers = false;
        show3dDistEndSelMarkers = false;
        show3dElevMarkers = false;
        show3dPlaceMarkers = false;
        show3dGradMarkers = false;
        show3dWayMarkers = false;
        switch (this.selectedOptions[0].__data__.i) {
            case 0:
                show3dDistMarkers = true;
                break;
            case 1:
                show3dDistEndMarkers = true;
                break;
            case 5:
                show3dDistSelMarkers = true;
                break;
            case 6:
                show3dDistEndSelMarkers = true;
                break;
            case 7:
                show3dWayMarkers = true;
                break;
            case 2:
                show3dElevMarkers = true;
                break;
            case 3:
                show3dPlaceMarkers = true;
                break;
            case 4:
                show3dGradMarkers = true;
                /*//if (placeMarkers == null) {
                  if (typeof(segment.maxLat) === 'undefined') {
                    var ext = d3.extent(segment.latlngArr.filter(function(d, i) {
                      return i % 2 == 0 && (typeof(p3dStart) === 'undefined' || i / 2 >= p3dStart) && (typeof(p3dEnd) === 'undefined' || i / 2 < p3dEnd)
                    }));
                    segment.minLat = ext[0];
                    segment.maxLat = ext[1];
                    ext = d3.extent(segment.latlngArr.filter(function(d, i) {
                      return i % 2 == 1 && (typeof(p3dStart) === 'undefined' || i / 2 >= p3dStart) && (typeof(p3dEnd) === 'undefined' || i / 2 < p3dEnd)
                    }));
                    segment.minLng = ext[0];
                    segment.maxLng = ext[1];
                  }
                  getGeoNames(segment.maxLat, segment.minLat, segment.maxLng, segment.minLng);
                } else {
                  set3dSegment();
                }
                return;*/
                break;
        }
        if (isTS && typeof(p3dDrawTSProfile) !== 'undefined') {
            p3dDrawTSProfile()
        } else {
            set3dSegment();
        }
    })
    .selectAll('option')
    .data([{
        i: 4,
        n: 'Gradients',
        d: 'Gradients'
    }, {
        i: 0,
        n: 'Dist |<',
        d: 'Distance from start (full)'
    }, {
        i: 1,
        n: 'Dist >|',
        d: 'Distance to end (full)'
    }, {
        i: 5,
        n: 'Dist |<',
        d: 'Distance from start (selection)'
    }, {
        i: 6,
        n: 'Dist >|',
        d: 'Distance to end (selection)'
    }, {
        i: 2,
        n: 'Elev',
        d: 'Main points of elevation'
    }, {
        i: 3,
        n: 'Places',
        d: 'Place names'
    }, {
        i: 10,
        n: 'None',
        d: 'No markers'
    }].filter(function(d) {
        return pageType3d == 'segment' && (d.i == 5 || d.i == 6) ? false : true
    }))
    .enter()
    .append('option')
    .property("selected", function(d) {
        return (d.i === 10)
    })
    .text(function(d) {
        return d.d;
    });

if (document.getElementById('detailRange') != null) {
    document.getElementById('detailRange').max = 0.75;
}

if (d3.select('#swapImgSize').text('Generate new image').node() != null) {
    d3.select(d3.select('#swapImgSize').text('Generate new image').node().parentElement).select('p').html('Save the image below to your device to share. <strong>Please leave the logos in place!</strong>');
}

/*d3.select('#svgWrapper').style('cursor', 'pointer')
.append('img')
.attr({
  'src':'http://cf.veloviewer.com/img/touch100.png',
  'id':'touchImg',
  'class':'shake',
  'style':'position:absolute;margin:auto;top:0;bottom:0;left:0;right:0'
})*/
