sumolib.shapes.polygon

  1# Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
  2# Copyright (C) 2012-2026 German Aerospace Center (DLR) and others.
  3# This program and the accompanying materials are made available under the
  4# terms of the Eclipse Public License 2.0 which is available at
  5# https://www.eclipse.org/legal/epl-2.0/
  6# This Source Code may also be made available under the following Secondary
  7# Licenses when the conditions for such availability set forth in the Eclipse
  8# Public License 2.0 are satisfied: GNU General Public License, version 2
  9# or later which is available at
 10# https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
 11# SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
 12
 13# @file    polygon.py
 14# @author  Daniel Krajzewicz
 15# @author  Melanie Knocke
 16# @author  Michael Behrisch
 17# @date    2012-12-04
 18
 19from __future__ import absolute_import
 20
 21from xml.sax import handler, parse
 22from .. import color
 23from .. import miscutils
 24
 25
 26def getBoundingBox(shape):
 27    xmin = shape[0][0]
 28    xmax = shape[0][0]
 29    ymin = shape[0][1]
 30    ymax = shape[0][1]
 31    for p in shape[1:]:
 32        xmin = min(xmin, p[0])
 33        xmax = max(xmax, p[0])
 34        ymin = min(ymin, p[1])
 35        ymax = max(ymax, p[1])
 36    return xmin, ymin, xmax, ymax
 37
 38
 39class Polygon:
 40
 41    def __init__(self, id, type=None, color=None, layer=None, fill=None,
 42                 shape=None, geo=None, angle=None, lineWidth=None, imgFile=None, color_str=None):
 43        self.id = id
 44        self.type = type
 45        self.color = color
 46        if layer is not None:
 47            layer = miscutils.intIfPossible(float(layer))
 48        self.layer = layer
 49        self.fill = fill
 50        self.shape = shape
 51        self.geo = geo
 52        self.angle = angle
 53        self.lineWidth = lineWidth
 54        self.imgFile = imgFile
 55        self.attributes = {}
 56        self.color_str = color_str
 57        if self.color is not None and self.color_str is None:
 58            if hasattr(self.color, "toXML"):
 59                self.color_str = self.color.toXML()
 60            else:
 61                self.color_str = self.color
 62
 63    def getBoundingBox(self):
 64        return getBoundingBox(self.shape)
 65
 66    def getShapeString(self):
 67        return " ".join([",".join(map(str, e)) for e in self.shape])
 68
 69    def toXML(self):
 70        ret = '<poly id="%s"' % self.id
 71        if self.type is not None:
 72            ret += ' type="%s"' % self.type
 73        if self.color is not None:
 74            if isinstance(self.color, color.RGBAColor):
 75                ret += ' color="%s"' % self.color.toXML()
 76            else:
 77                ret += ' color="%s"' % self.color
 78        if self.layer is not None:
 79            ret += ' layer="%s"' % self.layer
 80        if self.fill is not None:
 81            ret += ' fill="%s"' % self.fill
 82        if self.shape is not None:
 83            ret += ' shape="%s"' % self.getShapeString()
 84        if len(self.attributes) == 0:
 85            ret += '/>'
 86        else:
 87            ret += '>'
 88            for a in self.attributes:
 89                ret += '<param key="%s" value="%s"/>' % (a, self.attributes[a])
 90            ret += '</poly>'
 91        return ret
 92
 93    def __lt__(self, other):
 94        return self.id < other.id
 95
 96    def __repr__(self):
 97        return self.toXML()
 98
 99
100class PolygonReader(handler.ContentHandler):
101
102    def __init__(self, includeTaz=False):
103        self._includeTaz = includeTaz
104        self._id2poly = {}
105        self._polys = []
106        self._lastPoly = None
107
108    def startElement(self, name, attrs):
109        if name == 'poly' or (self._includeTaz and name == 'taz'):
110            cshape = []
111            for e in attrs['shape'].split():
112                p = e.split(",")
113                cshape.append((float(p[0]), float(p[1])))
114            if name == 'poly':
115                c = color.decodeXML(attrs['color'])
116                poly = Polygon(attrs['id'], attrs.get('type'), c,
117                               attrs.get('layer'), attrs.get('fill'), cshape,
118                               attrs.get('geo'), attrs.get('angle'),
119                               attrs.get('lineWidth'), attrs.get('imgFile'), attrs.get('color'))
120            else:
121                poly = Polygon(attrs['id'], color=attrs.get('color'), shape=cshape)
122            self._id2poly[poly.id] = poly
123            self._polys.append(poly)
124            self._lastPoly = poly
125        if name == 'param' and self._lastPoly is not None:
126            self._lastPoly.attributes[attrs['key']] = attrs['value']
127
128    def endElement(self, name):
129        if name == 'poly':
130            self._lastPoly = None
131
132    def getPolygons(self):
133        return self._polys
134
135
136def read(filenames, includeTaz=False):
137    pr = PolygonReader(includeTaz)
138    if isinstance(filenames, str):
139        filenames = [filenames]
140    for fn in filenames:
141        parse(miscutils.openz(fn), pr)
142    return pr.getPolygons()
def getBoundingBox(shape):
27def getBoundingBox(shape):
28    xmin = shape[0][0]
29    xmax = shape[0][0]
30    ymin = shape[0][1]
31    ymax = shape[0][1]
32    for p in shape[1:]:
33        xmin = min(xmin, p[0])
34        xmax = max(xmax, p[0])
35        ymin = min(ymin, p[1])
36        ymax = max(ymax, p[1])
37    return xmin, ymin, xmax, ymax
class Polygon:
40class Polygon:
41
42    def __init__(self, id, type=None, color=None, layer=None, fill=None,
43                 shape=None, geo=None, angle=None, lineWidth=None, imgFile=None, color_str=None):
44        self.id = id
45        self.type = type
46        self.color = color
47        if layer is not None:
48            layer = miscutils.intIfPossible(float(layer))
49        self.layer = layer
50        self.fill = fill
51        self.shape = shape
52        self.geo = geo
53        self.angle = angle
54        self.lineWidth = lineWidth
55        self.imgFile = imgFile
56        self.attributes = {}
57        self.color_str = color_str
58        if self.color is not None and self.color_str is None:
59            if hasattr(self.color, "toXML"):
60                self.color_str = self.color.toXML()
61            else:
62                self.color_str = self.color
63
64    def getBoundingBox(self):
65        return getBoundingBox(self.shape)
66
67    def getShapeString(self):
68        return " ".join([",".join(map(str, e)) for e in self.shape])
69
70    def toXML(self):
71        ret = '<poly id="%s"' % self.id
72        if self.type is not None:
73            ret += ' type="%s"' % self.type
74        if self.color is not None:
75            if isinstance(self.color, color.RGBAColor):
76                ret += ' color="%s"' % self.color.toXML()
77            else:
78                ret += ' color="%s"' % self.color
79        if self.layer is not None:
80            ret += ' layer="%s"' % self.layer
81        if self.fill is not None:
82            ret += ' fill="%s"' % self.fill
83        if self.shape is not None:
84            ret += ' shape="%s"' % self.getShapeString()
85        if len(self.attributes) == 0:
86            ret += '/>'
87        else:
88            ret += '>'
89            for a in self.attributes:
90                ret += '<param key="%s" value="%s"/>' % (a, self.attributes[a])
91            ret += '</poly>'
92        return ret
93
94    def __lt__(self, other):
95        return self.id < other.id
96
97    def __repr__(self):
98        return self.toXML()
Polygon( id, type=None, color=None, layer=None, fill=None, shape=None, geo=None, angle=None, lineWidth=None, imgFile=None, color_str=None)
42    def __init__(self, id, type=None, color=None, layer=None, fill=None,
43                 shape=None, geo=None, angle=None, lineWidth=None, imgFile=None, color_str=None):
44        self.id = id
45        self.type = type
46        self.color = color
47        if layer is not None:
48            layer = miscutils.intIfPossible(float(layer))
49        self.layer = layer
50        self.fill = fill
51        self.shape = shape
52        self.geo = geo
53        self.angle = angle
54        self.lineWidth = lineWidth
55        self.imgFile = imgFile
56        self.attributes = {}
57        self.color_str = color_str
58        if self.color is not None and self.color_str is None:
59            if hasattr(self.color, "toXML"):
60                self.color_str = self.color.toXML()
61            else:
62                self.color_str = self.color
id
type
color
layer
fill
shape
geo
angle
lineWidth
imgFile
attributes
color_str
def getBoundingBox(self):
64    def getBoundingBox(self):
65        return getBoundingBox(self.shape)
def getShapeString(self):
67    def getShapeString(self):
68        return " ".join([",".join(map(str, e)) for e in self.shape])
def toXML(self):
70    def toXML(self):
71        ret = '<poly id="%s"' % self.id
72        if self.type is not None:
73            ret += ' type="%s"' % self.type
74        if self.color is not None:
75            if isinstance(self.color, color.RGBAColor):
76                ret += ' color="%s"' % self.color.toXML()
77            else:
78                ret += ' color="%s"' % self.color
79        if self.layer is not None:
80            ret += ' layer="%s"' % self.layer
81        if self.fill is not None:
82            ret += ' fill="%s"' % self.fill
83        if self.shape is not None:
84            ret += ' shape="%s"' % self.getShapeString()
85        if len(self.attributes) == 0:
86            ret += '/>'
87        else:
88            ret += '>'
89            for a in self.attributes:
90                ret += '<param key="%s" value="%s"/>' % (a, self.attributes[a])
91            ret += '</poly>'
92        return ret
class PolygonReader(xml.sax.handler.ContentHandler):
101class PolygonReader(handler.ContentHandler):
102
103    def __init__(self, includeTaz=False):
104        self._includeTaz = includeTaz
105        self._id2poly = {}
106        self._polys = []
107        self._lastPoly = None
108
109    def startElement(self, name, attrs):
110        if name == 'poly' or (self._includeTaz and name == 'taz'):
111            cshape = []
112            for e in attrs['shape'].split():
113                p = e.split(",")
114                cshape.append((float(p[0]), float(p[1])))
115            if name == 'poly':
116                c = color.decodeXML(attrs['color'])
117                poly = Polygon(attrs['id'], attrs.get('type'), c,
118                               attrs.get('layer'), attrs.get('fill'), cshape,
119                               attrs.get('geo'), attrs.get('angle'),
120                               attrs.get('lineWidth'), attrs.get('imgFile'), attrs.get('color'))
121            else:
122                poly = Polygon(attrs['id'], color=attrs.get('color'), shape=cshape)
123            self._id2poly[poly.id] = poly
124            self._polys.append(poly)
125            self._lastPoly = poly
126        if name == 'param' and self._lastPoly is not None:
127            self._lastPoly.attributes[attrs['key']] = attrs['value']
128
129    def endElement(self, name):
130        if name == 'poly':
131            self._lastPoly = None
132
133    def getPolygons(self):
134        return self._polys

Interface for receiving logical document content events.

This is the main callback interface in SAX, and the one most important to applications. The order of events in this interface mirrors the order of the information in the document.

PolygonReader(includeTaz=False)
103    def __init__(self, includeTaz=False):
104        self._includeTaz = includeTaz
105        self._id2poly = {}
106        self._polys = []
107        self._lastPoly = None
def startElement(self, name, attrs):
109    def startElement(self, name, attrs):
110        if name == 'poly' or (self._includeTaz and name == 'taz'):
111            cshape = []
112            for e in attrs['shape'].split():
113                p = e.split(",")
114                cshape.append((float(p[0]), float(p[1])))
115            if name == 'poly':
116                c = color.decodeXML(attrs['color'])
117                poly = Polygon(attrs['id'], attrs.get('type'), c,
118                               attrs.get('layer'), attrs.get('fill'), cshape,
119                               attrs.get('geo'), attrs.get('angle'),
120                               attrs.get('lineWidth'), attrs.get('imgFile'), attrs.get('color'))
121            else:
122                poly = Polygon(attrs['id'], color=attrs.get('color'), shape=cshape)
123            self._id2poly[poly.id] = poly
124            self._polys.append(poly)
125            self._lastPoly = poly
126        if name == 'param' and self._lastPoly is not None:
127            self._lastPoly.attributes[attrs['key']] = attrs['value']

Signals the start of an element in non-namespace mode.

The name parameter contains the raw XML 1.0 name of the element type as a string and the attrs parameter holds an instance of the Attributes class containing the attributes of the element.

def endElement(self, name):
129    def endElement(self, name):
130        if name == 'poly':
131            self._lastPoly = None

Signals the end of an element in non-namespace mode.

The name parameter contains the name of the element type, just as with the startElement event.

def getPolygons(self):
133    def getPolygons(self):
134        return self._polys
def read(filenames, includeTaz=False):
137def read(filenames, includeTaz=False):
138    pr = PolygonReader(includeTaz)
139    if isinstance(filenames, str):
140        filenames = [filenames]
141    for fn in filenames:
142        parse(miscutils.openz(fn), pr)
143    return pr.getPolygons()