コード例 #1
0
    def _calcEdgeTrim(self, A, currentEdge):
        T = np.array([[0, 1], [-1, 0]])
        trimA = 0
        endA, dA = getDirection(currentEdge['geom'], firstNode(A))
        # if firstNode(A):
        #     coordsA = np.array(currentEdge['geom'].coords[:2])
        # else:
        #     coordsA = np.array(currentEdge['geom'].coords[-1:-3:-1])
        # dA = np.diff(coordsA, axis=0)
        # dA /= np.linalg.norm(dA)
        # dA = np.squeeze(dA)
        self.graph.node[A]['dir'] = dA
        for other in self.graph.node[A]['order']:
            if (A, other) in self.graph.edges:
                for route in self.graph.edges[A, other]['routes']:
                    if route not in currentEdge['offsets']:
                        continue
                    otherEdge = self.graph.edges[fullEdge(other)]
                    if firstNode(A):
                        offsetA = currentEdge['offsets'][route]
                    else:
                        offsetA = -currentEdge['offsets'][route]
                    pA = (endA + offsetA * np.dot(dA, T))

                    endO, dO = getDirection(otherEdge['geom'], firstNode(other))
                    if firstNode(other):
                    #     coordsO = np.array(otherEdge['geom'].coords[:2])
                        offsetO = otherEdge['offsets'][route]
                    else:
                    #     coordsO = np.array(otherEdge['geom'].coords[-1:-3:-1])
                        offsetO = -otherEdge['offsets'][route]
                    #
                    # dO = np.diff(coordsO, axis=0)
                    # dO /= np.linalg.norm(dO)
                    # dO = np.squeeze(dO)
                    pO = (endO + offsetO * np.dot(dO, T))
                    cosAO = np.dot(dA, dO)
                    sinAO = np.cross(dA, dO)
                    tAO2 = abs(sinAO) / max(1 + cosAO, 1e-300)  # trig ids!
                    setback = self.minRadius / tAO2
                    adjustment = (offsetA * cosAO - offsetO) / sinAO
                    trim = setback + adjustment + self.minRadius

                    #angle = np.arcsin(abs(sinAO))
                    self.graph.edges[A, other]['angle'] = np.arctan2(abs(sinAO), cosAO)
                    if np.sin(self.maxAngle) > np.sin(self.graph.edges[A, other]['angle']):
                    #if trim > 5 * self.minRadius:  # arbitrary
                        d = np.linalg.norm(pA - pO)
                        offsetA = max(currentEdge['offsets'].values()) - min(currentEdge['offsets'].values())
                        offsetO = max(otherEdge['offsets'].values()) - min(otherEdge['offsets'].values())
                        radius = self.minRadius + (offsetA + offsetO) / 2.
                        trim = np.sqrt(radius ** 2. -
                                       max(0, radius - d * .5) ** 2.) * 1.1
                    trimA = max(trimA, trim)
        return trimA
コード例 #2
0
    def _buildGraph(self, graph):
        for u in graph:
            connections = []
            directions = []
            for v in graph[u]:
                for i in graph[u][v]:
                    a, b, i = edgeKey(u, v, i)
                    A, B = (a, (a, b, i)), (b, (a, b, i))
                    geom = graph[a][b][i]['geom'].simplify(1e-3)
                    if (A, B) not in self.graph.edges:
                        self.graph.add_edge(A, B, geom=geom,
                                            junction=False, routes={},
                                            sides={})
                    connections.append((a, b, i))
                    __, d = getDirection(geom, firstNode((u, (a, b, i))))
                    #print A, B, d
                    directions.append(math.atan2(d[1], d[0]))
                    self.graph.node[(u, (a, b, i))]['order'] = []
            for A, B in combinations(connections, 2):
                self.graph.add_edge((u, A), (u, B), junction=True, routes={})

            curA = None
            numDirs = 0
            for d, A in cycle(sorted(zip(directions, connections))):
                if curA is None:
                    if numDirs == len(connections):
                        break
                    curA = A
                    numDirs += 1
                elif A == curA:
                    curA = None
                else:
                    self.graph.node[(u, curA)]['order'].append((u, A))
コード例 #3
0
    def _compareRoutes(self, startEdge, route1, route2):
        total = 0
        visited = {}
        startEdge = fullEdge(startEdge[0])
        queue = [(startEdge, 1)]
        while queue:
            (A, B), direction = queue.pop()
            if (A, B) in visited:
                continue
            visited[A, B] = direction
            total -= direction * self._getSideVotes(A, route1, route2)
            total += direction * self._getSideVotes(B, route1, route2)

            a, (u, v, i) = A
            for nextNode in self.graph.nodes[A]['order']:
                nextEdge = fullEdge(nextNode)
                if (route1 in self.graph.edges[nextEdge]['routes'] and
                        route2 in self.graph.edges[nextEdge]['routes']):
                    if firstNode(nextNode):
                        queue.insert(0, (nextEdge, -direction))
                    else:
                        queue.insert(0, (nextEdge, direction))

            b, (u, v, i) = B
            for nextNode in self.graph.nodes[B]['order']:
                nextEdge = fullEdge(nextNode)
                if (route1 in self.graph.edges[nextEdge]['routes'] and
                        route2 in self.graph.edges[nextEdge]['routes']):
                    if firstNode(nextNode):
                        queue.insert(0, (nextEdge, direction))
                    else:
                        queue.insert(0, (nextEdge, -direction))

        if total >= 0:  # break ties in favour of right
            side = 1
        else:
            side = -1
        for edge, direction in visited.items():
            self.graph.edges[edge]['sides'][route1, route2] = side * direction
            self.graph.edges[edge]['sides'][route2, route1] = -side * direction
コード例 #4
0
    def _sortRoutes(self):
        def edgeString(edge):
            (a, (a, b, i)), (b, (a, b, i)) = edge
            return '{}_{}_{}'.format(a, b, i)
        crossings = []
        seq_vars = {}
        pre_vars = {}

        problem = LpProblem('route_crossings')
        problem += 0, 'Minimize crossings'
        for edge in self.graph.edges:

            currentEdge = self.graph.edges[edge]
            edge = fullEdge(edge[0])
            if not currentEdge['junction']:
                routes = [r for r in sorted(currentEdge['routes'])
                          if self.schedule.GetRoute(r).route_color != '']
                N = len(routes)
                estr = 'edge_' + edgeString(edge) + '_{}'
                s = {r: LpVariable(estr.format(r), 0, N-1, cat='Integer')
                     for r in routes}
                seq_vars[edge] = s
                estr += '_{}'
                pre_vars[edge] = {}
                for (r1, r2) in combinations(routes, 2):
                    pre = LpVariable(estr.format(r1, r2), cat='Binary')
                    pre_vars[edge][r1, r2] = pre
                    problem += s[r1] + 1 <= s[r2] + N * (1 - pre), ''
                    problem += s[r2] + 1 <= s[r1] + N * pre, ''

        c_ctr = count()
        for edge in self.graph.edges:
            edge = fullEdge(edge[0])
            currentEdge = self.graph.edges[edge]
            if not currentEdge['junction']:
                routes = [r for r in currentEdge['routes']
                          if self.schedule.GetRoute(r).route_color != '']
                p_edge = pre_vars[edge]
                for end in edge:
                    currentNode = self.graph.node[end]
                    for other in currentNode['order']:
                        junction = self.graph.edges[end, other]
                        j_routes = [r for r in junction['routes'] if r in routes]
                        otherEdge = fullEdge(other)
                        p_other = pre_vars[otherEdge]
                        for r1, r2 in combinations(j_routes, 2):
                            if r2 < r1:
                                r1, r2 = r2, r1
                            if (r1, r2) not in p_edge or (r1, r2) not in p_other:
                                continue
                            c = LpVariable('c_{}'.format(c_ctr.next()), cat='Binary')
                            if firstNode(end) == firstNode(other):
                                problem += p_edge[r1, r2] + p_other[r1, r2] - 1 <= c, ''
                                problem += 1 - p_edge[r1, r2] - p_other[r1, r2] <= c, ''
                            else:
                                problem += p_edge[r1, r2] - p_other[r1, r2] <= c, ''
                                problem += p_other[r1, r2] - p_edge[r1, r2] <= c, ''
                            crossings.append(c)
                    for o1, o2 in combinations(currentNode['order'], 2):
                        j1 = self.graph.edges[end, o1]
                        j2 = self.graph.edges[end, o2]
                        jr1 = [r for r in j1['routes'] if r in routes]
                        jr2 = [r for r in j2['routes'] if r in routes]
                        for r1, r2 in product(jr1, jr2):
                            if r1 == r2:
                                continue
                            reverse = not firstNode(end)
                            if r1 > r2:
                                p = p_edge[r2, r1]
                                reverse = not reverse
                            else:
                                p = p_edge[r1, r2]
                            c = LpVariable('c_{}'.format(c_ctr.next()), cat='Binary')
                            if reverse:
                                problem += (1 - p == c), ''
                            else:
                                problem += p == c, ''
                            crossings.append(c*0.5)
        problem.setObjective(lpSum(crossings))
        #print problem
        problem.writeLP('crossings.lp')
        problem.solve()
        print lpvalue(problem.objective)

        # for c in crossings:
        #     print c.value()
        for edge, s_vars in seq_vars.items():
            currentEdge = self.graph.edges[edge]
            print edge
            for s, r in sorted([(s.value(), r) for (r, s) in s_vars.items()]):
                currentEdge['sequence'].append(r)
                print s, r
                currentEdge['width'] += self._getTotalThickness(edge, r)
コード例 #5
0
    def renderAsJson(self, filename):
        bb = self.schedule.GetStopBoundingBox()
        longitude = (bb[1] + bb[3]) / 2.
        zone = (int((longitude + 180)/6) % 60) + 1
        utm17 = pyproj.Proj(proj='utm', zone=zone, ellps='WGS84')
        features = []

        for edge in self.graph.edges:
            if not self.graph.edges[edge]['junction']:
                A, B = edge
                A, B = fullEdge(A)
                currentEdge = self.graph.edges[edge]
                #self.graph.edges[edge]['offsets'] = {}
                self.graph.node[A]['points'] = {}
                self.graph.node[B]['points'] = {}
                centreGeom = currentEdge['geom'].simplify(self.totalThickness / 2.)
                __, self.graph.node[A]['dir'] = getDirection(centreGeom, True)
                __, self.graph.node[B]['dir'] = getDirection(centreGeom, False)
                #offset = -self.graph.edges[edge]['width'] / 2.
                for route in currentEdge['sequence']:
                    route_color = self.schedule.GetRoute(route).route_color
                    #offset += self._getTotalThickness(edge, route) / 2.
                    offset = currentEdge['offsets'][route]
                    geom = centreGeom.parallel_offset(offset, 'left')
                    #self.graph.edges[edge]['offsets'][route] = offset
                    if offset >= 0:
                        self.graph.node[A]['points'][route] = geom.coords[0]
                        self.graph.node[B]['points'][route] = geom.coords[-1]
                    else:
                        self.graph.node[A]['points'][route] = geom.coords[-1]
                        self.graph.node[B]['points'][route] = geom.coords[0]
                    #offset += self._getTotalThickness(edge, route) / 2.
                    coord_list = [list(utm17(inverse=True, *c))
                                  for c in geom.coords]
                    features.append({"type": "Feature",
                                     "properties": {
                                            'stroke-width': self._getLineThickness(edge, route),
                                            'stroke': '#' + route_color,
                                            'stroke-opacity': 1,
                                            'route': route,
                                            'sides': str(self.graph.edges[edge]['sides'].items())
                                        },
                                     "geometry": {
                                        "type": "LineString",
                                        "coordinates": coord_list
                                     }})
        for edge in self.graph.edges:
            if self.graph.edges[edge]['junction']:
                A, B = edge
                currentEdge = self.graph.edges[edge]
                edgeA = self.graph.edges[fullEdge(A)]
                edgeB = self.graph.edges[fullEdge(B)]
                seqA = list(edgeA['sequence'])
                seqB = list(edgeB['sequence'])
                if firstNode(A) == firstNode(B):
                    seqB.reverse()
                # __, dA = getDirection(edgeA['geom'], firstNode(A))
                # __, dB = getDirection(edgeB['geom'], firstNode(B))
                dA = self.graph.node[A]['dir']
                dB = self.graph.node[B]['dir']
                # if self.maxAngle > currentEdge['angle']:
                #
                #
                #     continue
                sinAB = np.cross(dA, dB)
                cosAB = np.dot(dA, dB)
                angle = np.arctan2(abs(sinAB), cosAB)
                right = sinAB > 0
                if right and firstNode(A) or not right and not firstNode(A):
                    seqA.reverse()
                    seqB.reverse()
                routesA = [r for r in seqA if r in currentEdge['routes']]
                routesB = [r for r in seqB if r in currentEdge['routes']]
                routes = [r for r in currentEdge['routes']
                          if r in routesA and r in routesB]
                if not routes:
                    continue
                offsetA0 = edgeA['offsets'][routesA[0]]
                offsetB0 = edgeB['offsets'][routesB[0]]

                # lastRadius = self.minRadius - self._getTotalThickness(edge, routeIndexes[0][1]) / 2.
                # delta = 0
                radii = {}
                for r in routes:
                    offsetA = abs(offsetA0 - edgeA['offsets'][r])
                    offsetB = abs(offsetB0 - edgeB['offsets'][r])
                    radii[r] = self.minRadius + min(offsetA, offsetB)
                # for i, r in sorted(routeIndexes):
                #     thickness = self._getTotalThickness(edge, r)
                #     if i != lastIndex:
                #         lastRadius += delta
                #         delta = 0
                #     radii[r] = lastRadius + thickness / 2.
                #     delta += thickness
                #     lastIndex = i
                for route in self.graph.edges[edge]['routes']:
                    route_color = self.schedule.GetRoute(route).route_color
                    if route not in edgeA['sequence'] or route not in edgeB['sequence']:
                        continue
                    ptA = np.array(self.graph.node[A]['points'][route])
                    ptB = np.array(self.graph.node[B]['points'][route])
                    # offsetA = edgeA['offsets'][route]
                    # offsetB = edgeB['offsets'][route]
                    # if firstNode(A):
                    #     ptA = edgeA['geom'].interpolate(0, normalized=True)
                    #     dirA = getDirection(edgeA['geom'], 0)
                    # else:
                    #     ptA = edgeA['geom'].interpolate(1, normalized=True)
                    #     dirA = getDirection(edgeA['geom'], 1)
                    # if firstNode(B):
                    #     ptB = edgeB['geom'].interpolate(0, normalized=True)
                    #     dirB = getDirection(edgeB['geom'], 0)
                    # else:
                    #     ptB = edgeB['geom'].interpolate(1, normalized=True)
                    #     dirB = getDirection(edgeB['geom'], 1)
                    # ptA = Point(ptA.x - dirA[1] * offsetA, ptA.y + dirA[0] * offsetA)
                    # ptB = Point(ptB.x - dirB[1] * offsetB, ptB.y + dirB[0] * offsetB)
                    radius = radii[route]
                    corner = intersection(ptA, dA, ptB, dB)
                    setback = radius / np.tan(angle/2)
                    delta = abs(setback-radius)/radius
                    filler = ''
                    if np.sin(self.maxAngle) > np.sin(angle) or delta > 1:
                        # hack for now with straight line
                        dAB = ptB - ptA
                        dAB /= np.linalg.norm(dAB)
                        dAR = np.array([dA[1], -dA[0]])
                        dBR = np.array([dB[1], -dB[0]])
                        crossAAB = np.cross(dA, dAB)
                        crossBAB = np.cross(dB, dAB)
                        dotAAB = np.dot(dA, dAB)
                        dotBAB = np.dot(dB, dAB)
                        offsetsA = [o
                                    for (r, o) in edgeA['offsets'].items()
                                    if r in routes]
                        offsetsB = [o
                                    for (r, o) in edgeB['offsets'].items()
                                    if r in routes]
                        if crossAAB < 0:  # left
                            if firstNode(A):
                                offsetA0 = min(offsetsA)
                            else:
                                offsetA0 = max(offsetsA)
                        else:  # right
                            if firstNode(A):
                                offsetA0 = max(offsetsA)
                            else:
                                offsetA0 = min(offsetsA)
                            dAR = -dAR
                        if crossBAB > 0:  # left
                            if firstNode(B):
                                offsetB0 = min(offsetsB)
                            else:
                                offsetB0 = max(offsetsB)
                        else:  # right
                            if firstNode(B):
                                offsetB0 = max(offsetsB)
                            else:
                                offsetB0 = min(offsetsB)
                            dBR = - dBR

                        radiusA = self.minRadius + abs(offsetA0 - edgeA['offsets'][route])
                        radiusB = self.minRadius + abs(offsetB0 - edgeB['offsets'][route])
                        centreA = ptA + dAR * radiusA
                        angleA = np.arctan2(-dAR[1], -dAR[0])
                        centreB = ptB + dBR * radiusB
                        angleB = np.arctan2(-dBR[1], -dBR[0])

                        l = np.linalg.norm(centreA - centreB)
                        #if radius + radius <= l:
                        __, dC = getDirection(LineString([centreA, centreB]))
                        angleC = np.arctan2(dC[1], dC[0])
                        if (crossAAB > 0 and crossBAB > 0) or (crossAAB < 0 and crossBAB < 0):
                            thetaB = np.arccos((radiusA-radiusB)/l)
                            thetaA = thetaB
                        elif radiusA + radiusB <= l:
                            thetaA = np.arccos((radiusA+radiusB)/l)
                            thetaB = np.pi - thetaA
                        else:
                            thetaA = 0
                            thetaB = np.pi
                        if crossAAB > 0:  # left
                            angleAC = angleC + thetaA
                        else:
                            angleAC = angleC - thetaA
                        if crossBAB < 0:  # left
                            angleBC = angleC - thetaB
                        else:
                            angleBC = angleC + thetaB
                        while angleAC - angleA > np.pi:
                            angleAC -= 2 * np.pi
                        while angleA - angleAC > np.pi:
                            angleAC += 2 * np.pi
                        while angleBC - angleB > np.pi:
                            angleBC -= 2 * np.pi
                        while angleB - angleBC > np.pi:
                            angleBC += 2 * np.pi
                        pts = [centreA + radiusA * np.array([np.cos(a), np.sin(a)]) for a in np.linspace(angleA, angleAC, 5)]
                        pts += [centreB + radiusB * np.array([np.cos(a), np.sin(a)]) for a in np.linspace(angleBC, angleB, 5)]
                        pts.insert(0, ptA)
                        pts.append(ptB)
                        filler = str((angleA, angleAC, angleBC, angleB, crossAAB, crossBAB, dotAAB, dotBAB, angleC, thetaA, thetaB, radiusA, radiusB))
                        # else:
                        #     pts = [ptA, ptB]

                        # coord_list = [list(utm17(*ptA, inverse=True)),
                        #               list(utm17(*ptB, inverse=True))]
                    else:
                        # radius = radii[route]
                        # corner = intersection(ptA, dA, ptB, dB)
                        # setback = radius / np.tan(angle/2)
                        endA = corner + dA * setback
                        if right:
                            # if firstNode(A):
                            #     setback = radius / np.tan(currentEdge['angle']/2)
                            # else:
                            #     setback = radius * np.tan(currentEdge['angle']/2)
                            # endA = corner + dA * setback
                            dAR = np.array([-dA[1], dA[0]])
                            dBR = np.array([dB[1], -dB[0]])
                            angleA = np.arctan2(-dAR[1], -dAR[0])
                            angleB = np.arctan2(-dBR[1], -dBR[0])
                            if angleB > angleA:
                                angleB -= 2. * np.pi
                        else:
                            # if firstNode(A):
                            #     setback = radius * np.tan(currentEdge['angle']/2)
                            # else:
                            #     setback = radius / np.tan(currentEdge['angle']/2)
                            # endA = corner + dA * setback
                            dAR = np.array([dA[1], -dA[0]])
                            dBR = np.array([-dB[1], dB[0]])
                            angleA = np.arctan2(-dAR[1], -dAR[0])
                            angleB = np.arctan2(-dBR[1], -dBR[0])
                            if angleB < angleA:
                                angleB += 2. * np.pi
                        centre = endA + radius * dAR
                        steps = int(abs(angleA - angleB)*4)  # for now, point at least every .25 radians
                        angles = np.linspace(angleA, angleB, steps + 2)
                        pts = [centre + [radius*np.cos(a), radius*np.sin(a)]
                               for a in angles]
                        pts.append(ptB)
                        pts.insert(0, ptA)
                    coord_list = [list(utm17(*pt, inverse=True))
                                  for pt in pts if pt[0] < 1e29]
                    thickness = min(self._getLineThickness(edgeA, route),
                                    self._getLineThickness(edgeB, route))
                    features.append({"type": "Feature",
                                     "properties": {
                                            'stroke-width': thickness,
                                            'stroke': '#' + route_color,
                                            'stroke-opacity': 1,
                                            'angles': filler
                                            # 'setback': min(setback, 1e30),
                                            # 'radius': radii[route]
                                        },
                                     "geometry": {
                                        "type": "LineString",
                                        "coordinates": coord_list
                                     }})


        data = {"type": "FeatureCollection",
                "features": features}
        with open(filename, 'w') as fp:
            json.dump(data, fp)
コード例 #6
0
    def _collapseEdges(self):
        toKill = True
        while toKill:
            toKill = []
            for edge in self.graph.edges:
                currentEdge = self.graph.edges[edge]
                if not currentEdge['junction']:
                    totalTrim = 0
                    for A in edge:
                        trim = self._calcEdgeTrim(A, currentEdge)
                        self.graph.node[A]['trim'] = trim
                        totalTrim += trim
                    if totalTrim > currentEdge['geom'].length:
                        toKill.append(edge)
            skipped = []
            for edge in toKill:
                A, B = edge
                orderA = self.graph.node[A]['order']
                orderB = self.graph.node[B]['order']
                killingA = False
                killingB = False
                for O in orderA:
                    otherEdge = fullEdge(O)
                    if otherEdge in toKill:
                        killingA = True
                        break
                for O in orderB:
                    otherEdge = fullEdge(O)
                    if otherEdge in toKill:
                        killingB = True
                        break
                # if killingA and killingB:
                #     skipped.append(edge)
                for endA, endB in product(orderA, orderB):
                    routesA = self.graph.edges[A, endA]['routes']
                    routesB = self.graph.edges[B, endB]['routes']
                    routes = {r: list(set(routesA[r]).intersection(routesB[r]))
                              for r in set(routesA).intersection(set(routesB))}
                    if (endA, endB) not in self.graph.edges:
                        self.graph.add_edge(endA, endB, junction=True, routes=routes)
                for endA in orderA:
                    order = self.graph.node[endA]['order']
                    i = order.index(A)
                    self.graph.node[endA]['order'] = order[0:i] + orderB + order[i+1:]
                for endB in orderB:
                    order = self.graph.node[endB]['order']
                    i = order.index(B)
                    self.graph.node[endB]['order'] = order[0:i] + orderA + order[i+1:]
                self.graph.remove_nodes_from(edge)
            for s in skipped:
                toKill.remove(s)

        for edge in self.graph.edges:
            if not self.graph.edges[edge]['junction']:
                geom = self.graph.edges[edge]['geom']
                starti, endi = 0, len(geom.coords)
                distances = [LineString([p, q]).length
                             for p, q in zip(geom.coords[:-1], geom.coords[1:])]
                distances = [0] + list(np.cumsum(distances))
                x, y = zip(*geom.coords)
                X = interp1d(distances, x)
                Y = interp1d(distances, y)
                for A in edge:
                    trim = self.graph.node[A]['trim']
                    if firstNode(A):
                        starti = bisect_left(distances, trim)
                        startP = [X(trim), Y(trim)]
                    else:
                        d = geom.length - trim
                        endi = bisect_left(distances, d)
                        endP = [X(d), Y(d)]
                coords = [startP] + geom.coords[starti:endi] + [endP]
                geom = LineString(coords).simplify(1)
                self.graph.edges[edge]['geom'] = geom