Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Display statistics of street surface #45

Open
matzqgii235 opened this issue Jun 25, 2016 · 9 comments
Open

Display statistics of street surface #45

matzqgii235 opened this issue Jun 25, 2016 · 9 comments
Milestone

Comments

@matzqgii235
Copy link

First of all, thank you for the great software you have written.

Other web-based routing tools (komoot, openrouteservice) display the amount of unpaved, cobbled, etc percentage of a route. This is a great way of telling how much energy one will need to cycle a planned route beforehand. The mentioned tools also give statistics of how much cycle path the route uses which is obviously an important measure.

I propose to create separate columns in the csv file for the surface and path type, so this statistics is easily generated using excel. If it could be directly integrated into the web-interface, it would be even better.

@nrenner
Copy link
Owner

nrenner commented Jul 19, 2016

I agree tag statictics would be useful, and it has been requested before.

OpenRouteService has just added this recently. The frontend is open source and also MIT licensed, so maybe the code could be shared or extracted into a plugin.

A vertical bar chart instead of such a line chart might be more intuitive, but also would consume more screen space.

We had tags in separate columns before, but that leads to a big, unhandy table with lots of emtpy cells, and I'm a bit reluctant to seperating out specific columns. So I would rather integrate some kind of chart, but probably not for the upcoming version. Related feature request: #19.

edit:

@nrenner nrenner added this to the later milestone Jul 19, 2016
@matzqgii235
Copy link
Author

I just browsed the OpenRouteService sources.

I'm not sure whether it is helpful to post this here (delete my post if not), but the relevant functions seem to be the following in ui.js:

function updateSurfaceSteepness(results, mapFeatureIds, mapLayer, totalDistance) {
    //var lang = preferences.language;
    var WayTypeResult = calculateChart(results, mapFeatureIds, "WayType", totalDistance);
    var WaySurfaceResult = calculateChart(results, mapFeatureIds, "WaySurface", totalDistance);
    var WaySteepnessResult = calculateSteepness(results);
    horizontalBarchart('layerRouteLines', list.divWayTypes, list.listWayTypesContainer, WayTypeResult, list.WayTypeColors);
    horizontalBarchart('layerRouteLines', list.divSurfaceTypes, list.listSurfaceTypesContainer, WaySurfaceResult, list.SurfaceTypeColors);
    horizontalBarchart('layerSteepnessLines', list.divSteepnessTypes, list.listSteepnessTypesContainer, WaySteepnessResult);
    $('#routeTypesContainer').show();
}

/**
 * displays way and surface type in horizontal barcharts
 * @param data: calculate type information for way and surface types
 * @param types: div element
 * @param list: div list element for type
 */
function horizontalBarchart(layer, types, list, data, colors) {
    d3.select(types).selectAll("svg").remove();
    var tip = d3.tip().attr('class', 'd3-tip').offset([-10, 0]).html(function(d) {
        var dist = util.convertDistanceFormat(d.distance, preferences.distanceUnit);
        return d.percentage + '% ' + d.typetranslated + ' (' + dist[1] + ' ' + dist[2] + ')';
    });
    var margin = {
            top: 0,
            right: 0,
            bottom: 0,
            left: 0
        },
        width = 315 - margin.left - margin.right,
        height = 24 - margin.top - margin.bottom;
    var y = d3.scale.ordinal().rangeRoundBands([height, 0]);
    var x = d3.scale.linear().rangeRound([0, width]);
    var yAxis = d3.svg.axis().scale(y).orient("left");
    var xAxis = d3.svg.axis().scale(x).orient("bottom");
    var svg = d3.select(types).append("svg").attr("width", width).attr("height", height).append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");
    y.domain([0]);
    x.domain([0, data[Object.keys(data)[Object.keys(data).length - 1]].y1]);
    svg.selectAll("rect").data(data).enter().append("rect").attr("height", 24).attr("x", function(d) {
        return x(d.y0) / 1;
    }).attr("width", function(d) {
        return x(d.y1) / 1 - x(d.y0) / 1;
    }).attr("title", function(d) {
        return (d.y1 - d.y0) + "% : " + d.typetranslated;
    }).style("fill", function(d, i) {
        if (colors) {
            return colors[i];
        } else {
            return d.color;
        }
    }).on('mouseover', function(d) {
        handleHighlightTypes(d.ids, layer);
        tip.show(d);
    }).on('mouseout', function(d) {
        handleResetTypes(d.ids, layer);
        tip.hide(d);
    }).on('click', function(d) {
        handleClickRouteIds(d.ids, layer);
    });
    $(list).empty();
    $(list).append("<ul></ul>");
    for (var i = 0; i < data.length; i++) {
        var li = $('<li>');
        li.html(data[i].percentage + '% ' + data[i].typetranslated);
        li.wrapInner('<span />');
        if (colors) {
            li.css('color', colors[i]);
        } else {
            li.css('color', data[i].color);
        }
        if (i !== "type" && i !== "total") $(list + "> ul").append(li);
    }
    svg.call(tip);
}


/** 
 * calculates percentages for waytypes and waysurfaces
 * @param types: either waytype or waysurface
 * @return WayTypesObject: Object containing Names and Percetages
 */
function calculateChart(results, featureIds, types, distArrAll) {
    var information, typelist, type;
    // keep route feature ids remove corner ids
    featureIds = featureIds.filter(function(el, index) {
        return index % 2 === 0;
    });
    if (types == "WayType") {
        information = util.getElementsByTagNameNS(results, namespaces.xls, 'WayTypeList')[0];
        information = util.getElementsByTagNameNS(results, namespaces.xls, 'WayType');
        typelist = [];
        for (type in list.WayType) {
            typelist.push({
                type: list.WayType[type],
                typetranslated: preferences.translate(list.WayType[type]),
                distance: 0,
                ids: [],
                segments: [],
                percentage: 0,
                y0: 0,
                y1: 0
            });
        }
    } else if (types == "WaySurface") {
        information = util.getElementsByTagNameNS(results, namespaces.xls, 'WaySurfaceList')[0];
        information = util.getElementsByTagNameNS(results, namespaces.xls, 'WaySurface');
        typelist = [];
        for (type in list.SurfaceType) {
            typelist.push({
                type: list.SurfaceType[type],
                typetranslated: preferences.translate(list.SurfaceType[type]),
                distance: 0,
                ids: [],
                segments: [],
                percentage: 0,
                y0: 0,
                y1: 0
            });
        }
    }
    var totaldistance = util.getElementsByTagNameNS(results, namespaces.xls, 'RouteSummary')[1];
    totaldistance = util.getElementsByTagNameNS(results, namespaces.xls, 'TotalDistance')[0];
    var totaldistancevalue = totaldistance.getAttribute('value');
    var stopoverCnt = 0;
    var stopoverDiff = 0;
    var toPrev;
    $A(information).each(function(WayType, i) {
        var fr = util.getElementsByTagNameNS(WayType, namespaces.xls, 'From')[0];
        fr = parseInt(fr.textContent);
        // stopovers are counted as segments and have to be filtered
        if (i > 0) {
            if (fr > (toPrev + 1)) {
                stopoverDiff = stopoverDiff + (fr - toPrev - 1);
            }
        }
        //1-2    5-6     10-11
        //       3-4     5-6
        //so:0   so: 2   so: 5 
        var to = util.getElementsByTagNameNS(WayType, namespaces.xls, 'To')[0];
        to = parseInt(to.textContent);
        toPrev = to;
        var typenumber = util.getElementsByTagNameNS(WayType, namespaces.xls, 'Type')[0];
        typenumber = typenumber.textContent;
        if (fr == to) {
            typelist[typenumber].distance += distArrAll[fr - stopoverDiff];
            typelist[typenumber].segments.push(parseInt(fr - stopoverDiff));
            typelist[typenumber].ids.push(featureIds[fr - stopoverDiff]);
        } else {
            for (fr; fr <= to; fr++) {
                typelist[typenumber].distance += distArrAll[fr - stopoverDiff];
                typelist[typenumber].segments.push(parseInt(fr - stopoverDiff));
                typelist[typenumber].ids.push(featureIds[fr - stopoverDiff]);
            }
        }
    });
    var a = 0;
    var y0 = 0;
    for (type in typelist) {
        if (typelist[type].distance > 0) {
            // consider percentages less than 1
            if (Math.floor(typelist[type].distance / totaldistancevalue * 100) < 1) {
                typelist[type].percentage = Math.floor(typelist[type].distance / totaldistancevalue * 100 * 10) / 10;
            } else {
                typelist[type].percentage = Math.floor(typelist[type].distance / totaldistancevalue * 100);
            }
            typelist[type].y0 = y0;
            typelist[type].y1 = y0 += +typelist[type].percentage;
        }
    }
    // remove elements without distance
    var typelistCleaned = typelist.filter(function(el) {
        return el.distance !== 0;
    });
    return typelistCleaned;
}

@michelschinz
Copy link

I would also find this very useful! What about displaying it like (paper) bike maps usually do, that is by using a dotted line instead of a plain one for unpaved sections?

@saesh
Copy link
Contributor

saesh commented Apr 4, 2017

Komoot does this as well, see bottom of the detail page: https://www.komoot.de/tour/15218919

@mjaschen
Copy link
Contributor

I've implemented this feature for my BRouter-Web instance at https://brouter.m11n.de/

There's a new “Analysis” tab in the sidebar with tables for way type, surface, and smoothness, showing the distances for each type. Track segments on the map are highlighted on hover/click.

It's not ready for a pull-request yet, because of some code duplication (I copied some methods from TrackMessages.js) and I'd like to refactor things first.

Example screenshot:

brouter-analysis

@nrenner
Copy link
Owner

nrenner commented May 25, 2020

Will be an eagerly awaited addition, looking forward to your PR!

@matzqgii235
Copy link
Author

matzqgii235 commented May 25, 2020 via email

@mjaschen
Copy link
Contributor

mjaschen commented Jun 6, 2020

However it didn't sort the table for the distances on each surface when I tried it.

A refactoring is in progress: the tables will be rendered using the datatable functionality as seen in the “Data” tab - that'll enable sorting among other things.

@mjaschen
Copy link
Contributor

mjaschen commented Jun 7, 2020

Pull Request #304 brings this feature to BRouter-Web :-)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

6 participants