#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""
@file    poi_alongRoads.py
@author  Ronald Nippold
@author  Daniel Krajzewicz
@author  Michael Behrisch
@date    2009-20-10
@version
@usage   poi_alongRoads.py <NET> <EDGE_ID>[,<EDGE_ID>]* <DISTANCE>

Spatial distribute of POIs along given edges on a given network.
Edges are separated with comma and without spaces in between.
The distance between POIs may be any positive real number

So far POIs are situated in the middle on all edges without regard to the type of the edge (street, junction).
 Edges may be given in arbitrary order, connected edges are found automatically
 Therefore: crossing chains of connected edges are not allowed -> this needs two different runs of this script
 Output is written in file 'pois.add.xml'

SUMO, Simulation of Urban MObility; see http://sumo.dlr.de/
Copyright (C) 2009-2015 DLR (http://www.dlr.de/) and contributors

This file is part of SUMO.
SUMO is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
"""

import os
import sys
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
import sumolib.net

if len(sys.argv) < 4:
    print >> sys.stderr, "Usage: " + \
        sys.argv[0] + " <NET> <EDGE_ID>[,<EDGE_ID>]* <DISTANCE>"
    sys.exit()
edgeList = sys.argv[2].split(',')
POIdist = float(sys.argv[3])


def findPred(connEdgesTmp, edgeList, success=0):
    for edge in edgeList:
        # check first element of connEdgesTmp for predecessor
        if connEdgesTmp[0][1] == edge[2]:
            connEdgesTmp.insert(0, edge)
            edgeList.remove(edge)
            success = 1
            return success
    return success


def findSucc(connEdgesTmp, edgeList, success=0):
    for edge in edgeList:
        # check last element of connEdgesTmp for successor
        if connEdgesTmp[len(connEdgesTmp) - 1][2] == edge[1]:
            connEdgesTmp.append(edge)
            edgeList.remove(edge)
            success = 1
            return success
    return success


def poiAppend(poi_edge, poi_nr, poi_x, poi_y, poi_buf):
    poi_buf.append("\t<poi id=\"" + str(poi_edge) + "-" + str(poi_nr) +
                   "\" type=\"default\" color=\"1,0,0\" layer=\"0\" x=\"" + str(poi_x) + "\" y=\"" + str(poi_y) + "\"/>")
    return

print("Reading net ...")
net = sumolib.net.readNet(sys.argv[1])

# find all predecessor/successor nodes for the given edges
nodeList = []
for edge in net._edges:
    for elm in sys.argv[2].split(','):
        if str(edge._id) == elm:
            data = []
            data.append(edge._id)
            data.append(edge._from._id)
            data.append(edge._to._id)
            data.append(edge._shape)
            nodeList.append(data)

# find connected edges
connEdges = []
connEdgesTmp = []
pred = 1
succ = 1
# real copy, not just reference because of loop in nodeList
tmpList = nodeList[:]
for elm in nodeList:
    if elm in tmpList:								# Has this edge already been processed?
        while pred == 1:							# search for predecessor
            if elm in tmpList:
                # don't look up the current edge in search list
                tmpList.remove(elm)
            if elm not in connEdgesTmp:
                # for the first element when searching for connected edges
                connEdgesTmp.append(elm)
            pred = findPred(connEdgesTmp, tmpList)
        while succ == 1:							# search for successor
            # all other things already done in predecessor
            succ = findSucc(connEdgesTmp, tmpList)
        connEdges.append(connEdgesTmp)
        connEdgesTmp = []							# clean up
        pred = 1									# reset
        succ = 1

# prepare output
POIbuf = []
POIbuf.append("<?xml version=\"1.0\"?>\n")
POIbuf.append("<!--\n\nPOIs for edges:\n\t" +
              str(sys.argv[2]) + "\n\non network:\n\t" + str(sys.argv[1]) + "\n-->\n")
POIbuf.append("<POIs>")
# space out POIs on the found (connected) edges
for elm in connEdges:
    # tmp array for storing all shape coordinate of connected edges
    shapes = []
    offset = 0										# Anything left from the edge before?
    for edge in elm:
        for p in edge[3]:
            #			shapes.append(p)
            # collect all shapes of connected edges
            shapes.append([p[0], p[1], edge[0]])
    for i in range(0, len(shapes) - 1):
        x1 = shapes[i][0]							# point at the beginning of the current edge
        y1 = shapes[i][1]
        x2 = shapes[i + 1][0]							# point at the end of the current edge
        y2 = shapes[i + 1][1]
        dX = x2 - x1
        dY = y2 - y1
        eucDist = pow(pow(dX, 2) + pow(dY, 2), 0.5)  # Euclidean distance
        normAscX = dX / eucDist						# normalized Euclidean distance in x
        normAscY = dY / eucDist						# normalized Euclidean distance in y
        xCur = x1									# the current POI position
        yCur = y1
        if i == 0:
            POIid = 1
            poiAppend(shapes[i][2], POIid, xCur, yCur, POIbuf)
            POIid = POIid + 1
            POIpos = POIdist
        else:
            if shapes[i][2] != shapes[i - 1][2]:		# begin of new edge
                POIid = 1
                # reset POI-position at the beginning of an edge
                POIpos = offset
        # check if first (new) point is on current edge, consider possible
        # offset from the edge before
        if eucDist + offset > POIdist:
            xCur = xCur + normAscX * (POIdist - offset)
            yCur = yCur + normAscY * (POIdist - offset)
            poiAppend(shapes[i][2], POIid, xCur, yCur, POIbuf)
            POIid = POIid + 1
            # always add POIdist, reset to current offset when changing the
            # edge
            POIpos = POIpos + POIdist
            offset = 0								# reset offset
            # other points - if possible - with normal spacing in between
            while pow(pow((x2 - xCur), 2) + pow((y2 - yCur), 2), 0.5) > POIdist:
                xCur = xCur + normAscX * POIdist
                yCur = yCur + normAscY * POIdist
                poiAppend(shapes[i][2], POIid, xCur, yCur, POIbuf)
                POIid = POIid + 1
                # always add POIdist, reset to current offset when changing the
                # edge
                POIpos = POIpos + POIdist
        # calculate offset (residual) for the next connected edge
        offset = offset + pow(pow((x2 - xCur), 2) + pow((y2 - yCur), 2), 0.5)
POIbuf.append("</POIs>")

print("writing output")
# finally write output in file
outfile = open('pois.add.xml', 'w')
for POIs in POIbuf:
    outfile.write(POIs + "\n")
outfile.close()
print("done")
