wiki:Help/Plugin/Scripting/Python/RCN_Route_Validator

Version 6 (modified by Polyglot, 10 years ago) ( diff )

latest version

#!/bin/jython
'''
CheckRouteOrNetworkOrCollectionOfRoutes.jy
- Validation of a rcn route relation

This code is released under the GNU General
Public License v2 or later.

The GPL v3 is accessible here:
http://www.gnu.org/licenses/gpl.html

The GPL v2 is accessible here:
http://www.gnu.org/licenses/old-licenses/gpl-2.0.html

It comes with no warranty whatsoever.

This code illustrates how to use Jython (Python in the scripting plugin of JOSM) to:
* loop over all members of a route relation
* find out whether the member is a node, a way or a relation
* add/change properties of a relation
* remove properties of a relation
* add members to a relation
* remove members from a relation
* sort all members backwards

* How to set an element selected

'''
from javax.swing import JOptionPane
from org.openstreetmap.josm import Main
import org.openstreetmap.josm.command as Command
import org.openstreetmap.josm.data.osm.Node as Node
import org.openstreetmap.josm.data.osm.Way as Way
import org.openstreetmap.josm.data.osm.Relation as Relation
import org.openstreetmap.josm.data.osm.TagCollection as TagCollection
import org.openstreetmap.josm.data.osm.DataSet as DataSet
import org.openstreetmap.josm.data.osm.RelationMember as RelationMember
import org.openstreetmap.josm.gui.dialogs.relation.DownloadRelationMemberTask as DownloadRelationMemberTask
import org.openstreetmap.josm.actions.DownloadReferrersAction as DownloadReferrersAction
import org.openstreetmap.josm.actions.AutoScaleAction as AutoScaleAction
import re, time

def getMapView():
    if Main.main and Main.main.map:
        return Main.main.map.mapView
    else:
        return None

def sortRouteRelation(route, nodesDict, beginAndEndnodes):
    nextNode = None
    routeToReturn = Relation(route)
    ##print dir (routeToReturn)
    #routeToReturn.clearOsmId()
    lowestNodeInt = 10000; lowPos = 0
    for nodeTuple in beginAndEndnodes:
        if nodeTuple[0] < lowestNodeInt:
            lowestNodeInt, nextNode = nodeTuple
    if not(nextNode):
        return route
    members=nodesDict[nextNode]
    processedMembers = {}
    #print len(nodesDict)
    #print dir(route)
    while lowPos<route.getMembersCount():
        #print 'in loop', routeToReturn
        if len(members) == 1:
            member = members[0]
        else:
            member = members[1]
            if member in processedMembers: member = members[0]
        processedMembers[member] = True
        way = member.getWay()
        routeToReturn.removeMembersFor(way)
        routeToReturn.addMember(lowPos,member)
        #routeToReturn.setModified(True)
        currentNode = nextNode
        nextNode = way.getNode(way.nodesCount-1).getId()
        if currentNode == nextNode:
            nextNode = way.getNode(0).getId()            
        if nextNode in nodesDict:
            members=nodesDict[nextNode]
        else:
            break
        del nodesDict[nextNode]
        #print len(nodesDict)
        if len(nodesDict)<3: break
        lowPos += 1
    #print 'before return', routeToReturn
    return routeToReturn

def checkForContinuity(membersList):
    listIsContinuous = True; prev_endnodes = []
    for member in membersList:
        if member.isWay():
            way = member.getWay()
            #print dir(way)
            #print way.getKeys()
            #print way.get('name')
            #print way.nodesCount-1, way
            if way.get('junction') == 'roundabout':
                endnodes = way.getNodes()
            else:
                if way.nodesCount:
                    endnodes = [way.getNode(0), way.getNode(way.nodesCount-1)]
                else:
                    endnodes = []
            foundTheNode = False
            for endnode in endnodes:
                #print endnode, prev_endnodes
                if endnode in prev_endnodes:
                    foundTheNode = True
            if prev_endnodes and not(foundTheNode):
                listIsContinuous = False
                #print way
                #print endnodes
                #print prev_endnodes
                break
            prev_endnodes = endnodes
    #print listIsContinuous
    return listIsContinuous

def checkRCNroute(route):
    commandsList = []
    reNumberDashNumber = re.compile(r'\d+-\d+')
    newRelation = Relation(route)
    relationChanged = False
    created_by = route.get('created_by')
    if created_by:
        print 'removing created_by'
        newRelation.remove('created_by')
        relationChanged = True
    name = route.get('name')
    if name:
        if reNumberDashNumber.match(name):
            print 'removing name when it is of the form ##-##'
            newRelation.remove('name')
            relationChanged = True
    else:
        name = ''
    ref = route.get('ref')
    if ref:
        if reNumberDashNumber.match(ref):
            print 'removing ref when it is of the form ##-##'
            newRelation.remove('ref')
            relationChanged = True
    else:
        ref = ''
    if relationChanged:
        commandsList.append(Command.ChangeCommand(route, newRelation))
        
        Main.main.undoRedo.add(Command.SequenceCommand("Removing name and/or ref and/or created_by" + name + '/' + ref, commandsList))
        commandsList = []

    fixme = route.get('fixme') == 'incomplete'
    rcn_refs = []; route_relation_names = []; memberslist = []; roleInNetwork = ''
    sameNodeNumberRepeated = False
    continuous_forward = True; continuous_backward = True
    allWaysgoingFromLowerToHigher = []; allWaysgoingFromHigherToLower = []; branch = None
    nodesForSorting = {}; beginAndEndNodes = []
    newRelation = Relation(route); commandslist = []; modified = False; i=0; roundabout = []

    for member in route.getMembers():
        if member.isWay():
            role = member.getRole()
            memberslist.append(member)
            way = member.getWay()
            #JOptionPane.showMessageDialog(Main.parent, 'way is selected')
            wayNodes = way.getNodes()
            #if len(wayNodes):
                #if way.get('junction') != 'roundabout':
                    #wayNodes = [way.getNode(0), way.getNode(way.nodesCount-1)]
            #else:
                #newRelation.removeMember(i)
                #modified = True

            notfoundyet = True
            for endnode in wayNodes:
                endnodeId = endnode.getId()
                if not(endnodeId in nodesForSorting):
                    nodesForSorting[endnodeId] = []
                nodesForSorting[endnodeId].append(member)

                rcn_ref = endnode.get('rcn_ref')
                if rcn_ref:
                    rcn_refAsInt = int(rcn_ref)
                    beginAndEndNodes.append([rcn_refAsInt, endnodeId])
                    if not(rcn_refAsInt in rcn_refs):
                        rcn_refs.append(rcn_refAsInt)
                    else:
                        sameNodeNumberRepeated = True
                    referrersForNode = endnode.getReferrers(); networkNotFoundForNode = True
                    for referrer in referrersForNode:
                        if referrer.getType() is dummy_relation.getType():
                            if referrer.get('type') in ['network'] and referrer.get('network') in ['rcn']:
                                networkNotFoundForNode = False
                                break
                    if networkNotFoundForNode:
                         Main.main.getCurrentDataSet().setSelected(endnode)
                         AutoScaleAction.zoomToSelection()
                         #selectedNode = mv.editLayer.data.getSelected()
                         print "Would download referrers for ", endnode.get('rcn_ref'), ' ', wayNodes
                         # DownloadReferrersAction.downloadReferrers(mv.editLayer, wayNodes)
                         referrersForNode = endnode.getReferrers()
                    for referrer in referrersForNode:
                        if referrer.getType() is dummy_relation.getType():
                            networkOrRouteType = referrer.get('type') in ['network'] #, 'route']
                            if referrer.get('network')=='rcn' and networkOrRouteType:
                                relname=referrer.get('name')
                                if relname:
                                    route_relation_names.append(relname)
                            elif not(referrer.get('type') in ['route']):
                                route_relation_names.append('Node not assigned to network yet')
            # build 2 structures to help check for continuity on ways
            if i==0:
                lastNodesBeforeSplit = [wayNodes]
            if role and role in ['forward', 'backward']:
                if not(branch):
                    if role in ['forward']:
                        lastNodesBeforeSplit = [wayNodes[0]]
                    else:
                        lastNodesBeforeSplit = [wayNodes[-1]]
                print 'wayNodes', wayNodes
                print 'lastNodesBeforeSplit', lastNodesBeforeSplit
                print way.getNodesCount(), 'startnode', wayNodes[0], 'endnode', wayNodes[-1]
                print 'roundabout', roundabout

                if role in ['forward'] and (wayNodes[-1] in lastNodesBeforeSplit or wayNodes[-1] in roundabout):
                    branch = 'HigherToLower'
                elif role in ['backward'] and wayNodes[0] in lastNodesBeforeSplit:
                    branch = 'HigherToLower'
                elif not(allWaysgoingFromHigherToLower) and wayNodes[-1].get('rcn_ref'):
                    branch = 'HigherToLower'
                elif not(branch == 'HigherToLower'):
                    branch = 'LowerToHigher'
                print branch
                if branch == 'LowerToHigher':
                    allWaysgoingFromLowerToHigher.append(member)
                elif branch == 'HigherToLower':
                    allWaysgoingFromHigherToLower.append(member)
            else:
                branch = None; roundabout = []
                allWaysgoingFromLowerToHigher.append(member)
                allWaysgoingFromHigherToLower.append(member)
            if way.get('junction') == 'roundabout':
                roundabout = way.getNodes()
        i+=1
    if modified:
        commandsList.append(Command.ChangeCommand(route, newRelation))
        Main.main.undoRedo.add(Command.SequenceCommand("Removing way which has no nodes", commandsList))
        commandsList = []
    #print allWaysgoingFromLowerToHigher
    if len(allWaysgoingFromLowerToHigher) > 1:
        continuous_forward = checkForContinuity(allWaysgoingFromLowerToHigher)
        #if not(continuous_forward) and not(fixme):
            #sortedRelation = sortRouteRelation(route, nodesForSorting, beginAndEndNodes)
            
            #print route
            #print sortedRelation
            #commandsList.append(Command.ChangeCommand(route, sortedRelation))
            #Main.main.undoRedo.add(Command.SequenceCommand("Sorted route relation", commandsList))            
            #allWaysgoingFromLowerToHigher = []; allWaysgoingFromHigherToLower = []
            #for member in route.getMembers():
                #if member.isWay():
                    #role = member.getRole()
                    #way = member.getWay()
                    #if role=='forward':
                        #allWaysgoingFromLowerToHigher.append(member)
                    #elif role=='backward':
                        #allWaysgoingFromHigherToLower.append(member)
                    #else:
                        #allWaysgoingFromLowerToHigher.append(member)
                        #allWaysgoingFromHigherToLower.append(member)
            #print allWaysgoingFromLowerToHigher
            #continuous_forward = checkForContinuity(allWaysgoingFromLowerToHigher)
        #else:
    else:
        continuous_forward = True
    if len(allWaysgoingFromHigherToLower) > 1:
        continuous_backward = checkForContinuity(reversed(allWaysgoingFromHigherToLower))
    else:
        continuous_backward = True
    # Drawing conclusions about rcn_refs
    print rcn_refs
    if sameNodeNumberRepeated:
        rcn_refs.append(rcn_refs[0])
    newNote = note = repr(rcn_refs)
    sortedRelation = route
    if len(rcn_refs) > 1:
        #newRelation = Relation(route)
        relationChanged = False

        if rcn_refs[0] > rcn_refs[1]:
            rcn_refs.sort()
            if len(memberslist)>1:
                print 'Flipping members order'
                for member in reversed(memberslist):
                    sortedRelation.addMember( sortedRelation.getMembersCount(), member)
                    sortedRelation.removeMember (0)
                commandsList.append(Command.ChangeCommand(route, sortedRelation))
                Main.main.undoRedo.add(Command.SequenceCommand("Flipping order of members", commandsList))
                commandsList = []
        note = route.get('note')
        newNote = str(rcn_refs[0]).zfill(2) + '-' + str(rcn_refs[1]).zfill(2)
        #print note, newNote
        if not(note) or note != newNote:
            if not(note): note = 'nothing'
            newRelation.put('note', newNote)
            relationChanged = True
            commandsList.append(Command.ChangeCommand(route, newRelation))
        
            Main.main.undoRedo.add(Command.SequenceCommand("Changing note from " + note + ' to ' + newNote, commandsList))
            commandsList = []

        if len(route_relation_names) > 1 and route_relation_names[0] != route_relation_names[1]:
            # print 'This is probably a CONNECTION to another network'
            print route_relation_names
            roleInNetwork = 'connection'
    else:
        print 'less than 2 end nodes with rcn_ref found'
    if fixme and not(continuous_forward or continuous_backward):
        print 'FIXME flag is INCOMPLETE'
    if continuous_forward:
        wikiCF = 'align="right" | continuous'
        #print 'route is continous in the forward direction'
    else:
        wikiCF = 'align="right" style="color:red" | NOT continuous'
        print 'route ',newNote,' is NOT CONTINUOUS in the forward direction'
        #print allWaysgoingFromLowerToHigher
    if continuous_backward:
        wikiCB = 'align="right" | continuous'
        #print 'route is continous in the backward direction'
    else:
        wikiCB = 'align="right" style="color:red" | NOT continuous'
        print 'route ',newNote,' is NOT CONTINUOUS in the backward direction'
        #print allWaysgoingFromHigherToLower
    print
    if fixme:
        wikiFM = 'style="color:red" | fixme' 
    else:
        wikiFM = ' | '
    if fixme or not(continuous_forward) or not(continuous_backward):
        problem = True
    else:
        problem = False
    return roleInNetwork, problem, '|align="right" | ' + note + '||align="right" | {{BrowseRoute|' + str(route.getId()) + '}}||align="right" ' + wikiFM + ' ||' + wikiCF + ' ||' + wikiCB + '\n'

def checkNetwork(network):
    name = network.get('name')
    print '********************************************'
    print name
    print '********************************************'
    #'===' + name + '===\n' + 
    wikiReportOnNodes = ('{| class="wikitable" align="left" style="margin:0 0 2em 2em;"\n|-\n|+' + name +
                         '\n|-\n!style="width:2.5em" | Node\n!Link\n! # Roads\n! # Relations\n|-\n')

    wikiReportOnRelations = ('{| class="wikitable" align="left" style="margin:0 0 2em 2em;"\n|-\n|+' + name +
                             '\n|-\n!style="width:2.5em" | note\n!link\n!fixme\n! forward\n! backward\n|-\n')
    i=1
    for networkmember in network.getMembers():
        if networkmember.isRelation():
            subrelation = networkmember.getRelation()
            if subrelation.hasIncompleteMembers():
                #JOptionPane.showMessageDialog(Main.parent, 'Please download all incomplete member of the route relations first')
                DownloadRelationMemberTask.run(DownloadRelationMemberTask(subrelation, subrelation.getIncompleteMembers(), mv.editLayer ))
                break
            roleFromRouteCheck, problemReported, wikiReport = checkRCNroute(subrelation)
            if problemReported:
                wikiReportOnRelations += wikiReport + '\n|-\n'
            #if roleFromRouteCheck:
            role = networkmember.getRole()
            if role != roleFromRouteCheck:
                print
                print 'Role in network is ', role
                print 'Maybe this should be: ', roleFromRouteCheck
        if networkmember.isNode():
            node = networkmember.getNode()
            rcn_ref = node.get('rcn_ref')
            expected_rcn_route_relations = node.get('expected_rcn_route_relations')
            if expected_rcn_route_relations:
                 expected_rcn_route_relations = int(expected_rcn_route_relations)
            else:
                expected_rcn_route_relations = 3
            if rcn_ref:
                #print rcn_ref
                referrersForNode = node.getReferrers()
                networkNotFoundForNode = True
                for referrer in referrersForNode:
                    if referrer.getType() is dummy_relation.getType():
                        if referrer.get('type') in ['network'] and referrer.get('network') in ['rcn']:
                            networkNotFoundForNode = False
                            break
                if networkNotFoundForNode:
                    #Main.main.getCurrentDataSet().setSelected(node)
                    #AutoScaleAction.zoomToSelection()
                    #selectedNode = mv.editLayer.data.getSelected()
                    #DownloadReferrersAction.downloadReferrers(mv.editLayer, selectedNode)
                    referrersForNode = node.getReferrers()

                rcnNetworkCountForNode = roads = 0
                for referrer in referrersForNode:
                    if referrer.getType() is dummy_way.getType():
                        if referrer.get('highway'): roads += 1
                        referrersForWay = referrer.getReferrers()
                        if len(referrersForWay) < 1:
                            Main.main.getCurrentDataSet().setSelected(referrer)
                            AutoScaleAction.zoomToSelection()
                            ##selectedWay = mv.editLayer.data.getSelected()
                            #DownloadReferrersAction.downloadReferrers(mv.editLayer, referrersForNode)
                            referrersForWay = referrer.getReferrers()
                        for wayreferrer in referrersForWay:
                            if wayreferrer.getType() is dummy_relation.getType():
                                memberslist = []
                                if wayreferrer.get('type') == 'route' and wayreferrer.get('network') == 'rcn': 
                                    rcnNetworkCountForNode+=1
                                for submember in network.getMembers():
                                    if submember.isRelation():
                                        subrelation = submember.getRelation()
                                        memberslist.append(subrelation.getId())
                                if not(wayreferrer.getId()):
                                    print dir(wayreferrer)
                                    break
                                routeId = wayreferrer.getId()
                                if not(routeId) or not(routeId in memberslist):
                                    if wayreferrer.get('network') == 'rcn':
                                        roleFromRouteCheck, problemReported, wikiReport = checkRCNroute(wayreferrer)
                                        print wikiReport
                                        if problemReported:
                                            wikiReportOnRelations += wikiReport + '\n|-\n'
                                        newRelation = Relation(network)
                                        newmember = RelationMember(roleFromRouteCheck, wayreferrer)
                                        print newRelation.getMembersCount(), ' ',i, ' ', newmember
                                        ##if i>newRelation.getMembersCount()
                                        newRelation.addMember( i, newmember)
                                        commandsList = []
                                        commandsList.append(Command.ChangeCommand(network, newRelation))
                                        note = wayreferrer.get('note')
                                        if not(note):
                                            note = ''
                                        Main.main.undoRedo.add(Command.SequenceCommand("Adding " + note + " to network", commandsList))
                                        commandsList = []
                                        print 'Added newly found RCN route relation to network: ', note
                                        i+=1
                if rcnNetworkCountForNode < expected_rcn_route_relations:
                    print 'Node ', rcn_ref, ' only has ', rcnNetworkCountForNode, ' rcn routes connected to it'
                    wikiReportOnNodes += '|align="right" | ' + rcn_ref + '||align="right" | {{Node|' + str(node.getId()) + '}}||align="right" |' + str(roads) + ' ||align="right" style="color:red" | ' + str(rcnNetworkCountForNode) + '\n|-\n'

        i+=1
    print
    wikiReportOnNodes += ('|-\n|}\n')
    wikiReportOnRelations += ('|-\n|}\n<br style="clear:left;" />\n')
    fh = open('C:/wikiReport.txt', 'a')
    fh.write(wikiReportOnNodes)
    fh.write(wikiReportOnRelations)
    fh.close()


dummy_way = Way()
dummy_relation = Relation()

mv = getMapView()
if mv and mv.editLayer and mv.editLayer.data:
    fh = open('C:/wikiReport.txt', 'w')
    fh.close()
    dummy_relation = Relation()
    selectedRelations = mv.editLayer.data.getSelectedRelations()

    if not(selectedRelations):
        JOptionPane.showMessageDialog(Main.parent, "Please select a route, network or collection relation")
    else:
        for relation in selectedRelations:
            #print relation
            #print relation.hasIncompleteMembers()
            if relation.hasIncompleteMembers():
                #JOptionPane.showMessageDialog(Main.parent, 'Please download all incomplete member of the relations first')
                DownloadRelationMemberTask.run(DownloadRelationMemberTask(relation, relation.getIncompleteMembers(), mv.editLayer ))
                break
            if relation.get('type') == 'route' and relation.get('network') == 'rcn':
                checkRCNroute(relation)
            if relation.get('type') == 'network' and relation.get('network') == 'rcn':
                checkNetwork(relation)
            if relation.get('type') == 'collection' and relation.get('network') == 'rcn':
                for networkmember in relation.getMembers():
                    if networkmember.isRelation():
                        subrelation = networkmember.getRelation()
                        if subrelation.hasIncompleteMembers():
                            #JOptionPane.showMessageDialog(Main.parent, 'Please download all incomplete member of the network relations first')
                            DownloadRelationMemberTask.run(DownloadRelationMemberTask(subrelation, subrelation.getIncompleteMembers(), mv.editLayer ))
                            #break
                        checkNetwork(subrelation)


Note: See TracWiki for help on using the wiki.