1# Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.org/sumo 2# Copyright (C) 2016-2019 German Aerospace Center (DLR) and others. 3# SUMOPy module 4# Copyright (C) 2012-2017 University of Bologna - DICAM 5# This program and the accompanying materials 6# are made available under the terms of the Eclipse Public License v2.0 7# which accompanies this distribution, and is available at 8# http://www.eclipse.org/legal/epl-v20.html 9# SPDX-License-Identifier: EPL-2.0 10 11# @file test_classman_classes.py 12# @author Joerg Schweizer 13# @date 14# @version $Id$ 15 16""" 17Test for callsman 18Provides test classes and some test functions for plugin. 19""" 20 21from classman import * 22from arrayman import * 23import xmlman as xm 24 25 26def on_event_delattr(attrconfig): 27 print 'EVENT: Attribute', attrconfig.attrname, 'will be deleted!!' 28 29 30def on_event_setattr(attrconfig): 31 print 'EVENT: Attribute', attrconfig.attrname, 'has been set to a new value', attrconfig.format_value() 32 33 34def on_event_getattr(attrconfig): 35 print 'EVENT: Attribute', attrconfig.attrname, 'has been retrieved the value', attrconfig.format_value() 36 37 38def on_event_additem(attrconfig, keys): 39 print 'EVENT: Attribute', attrconfig.attrname, ':added keys:', keys 40 41 42def on_event_delitem(attrconfig, keys): 43 print 'EVENT: Attribute', attrconfig.attrname, ':delete keys:', keys 44 45 46def on_event_setitem(attrconfig, keys): 47 print 'EVENT: Attribute', attrconfig.attrname, ':set keys:', keys 48 49 50def on_event_getitem(attrconfig, keys): 51 print 'EVENT: Attribute', attrconfig.attrname, ':get keys:', keys 52 53 54class Segments(ArrayObjman): 55 56 def __init__(self, ident='segments', parent=None, **kwargs): 57 58 self._init_objman(ident, parent=parent, xmltag=('segments', 'segment', 'ids_ref'), **kwargs) 59 60 self.add_col(ArrayConf('ids_ref', '', 61 dtype='object', 62 perm='r', 63 is_index=True, 64 name='ID Ref', 65 info='Ref ID', 66 xmltag='id_ref', 67 )) 68 69 self.add_col(ArrayConf('vertices', np.zeros((2, 3), float), 70 groupnames=['internal'], 71 perm='rw', 72 name='Vertex', 73 is_save=True, 74 info='Vertex coordinate vectors of points. with format [[[x11,y11,z11],[x12,y12,z12]],[[x21,y21,z21],[x22,y22,z122]],...]', 75 xmltag='vertex', 76 )) 77 78 self.add_col(IdsArrayConf('ids_parent', parent, 79 groupnames=['state'], 80 is_save=True, 81 name='ID '+parent.get_ident(), 82 info='ID of '+parent.get_name()+' object.', 83 xmltag='id_poly', 84 )) 85 86 87class Polylines (ArrayObjman): 88 def __init__(self, ident='polyline', parent=None, name='Polyline', 89 info='Polyline [ segid11, segid12,...]', **kwargs): 90 self._init_objman(ident, parent=parent, xmltag=('polylines', 'polyline', 'ids_osm'), **kwargs) 91 # print '__init__',self.get_name(),self.format_ident() 92 93 self.add_col(ArrayConf('ids_osm', '', 94 dtype='object', 95 perm='r', 96 is_index=True, 97 name='ID OSM', 98 info='Edge ID of OSM', 99 xmltag='id_osm', 100 )) 101 102 # initialize line segments 103 segments = Segments(parent=self) 104 self.add(ObjConf(segments, groupnames=['drawobjects'])) 105 106 # print ' segments',segments 107 # print ' self.segments',self.segments,type(self.segments) 108 # create table with id lists to segments 109 self.add_col(IdlistsArrayConf('ids_segments', segments, 110 groupnames=['elements'], 111 is_save=True, 112 name='IDs Segs', 113 info='List with IDs to Line segments.', 114 xmltag='segments', 115 )) 116 117 def draw(self, pointvertices, id_osm): 118 """ 119 pointvertices = [ 120 [0.0,0.0,0.0], 121 [0.2,0.0,0.0], 122 ] 123 """ 124 vertices = [] 125 print 'draw', self.ident 126 for i in xrange(1, len(pointvertices)): 127 vertices.append([pointvertices[i-1], pointvertices[i]]) 128 n_vert = len(vertices) 129 _id = self.add_row(ids_osm=id_osm) 130 cod = [] 131 #import string 132 clist = np.array(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p'], np.object) 133 print ' ', len(vertices), clist[:len(vertices)] 134 ids = self.segments.value.suggest_ids(len(vertices)) 135 ids_segs = self.segments.value.add_rows(ids=ids, vertices=vertices, ids_parent=n_vert*[_id], ids_ref=clist[ids]) 136 self.ids_segments[_id] = list(ids_segs) # put here list, otherwise numpy thinks it is a numeric array 137 return _id 138 139 140class Lines(ArrayObjman): 141 142 def __init__(self, ident, parent=None, **kwargs): 143 144 self._init_objman(ident, parent=parent, **kwargs) 145 146 self.add_col(ArrayConf('vertices', np.zeros((2, 3), float), 147 groupnames=['internal'], 148 perm='rw', 149 name='Vertex', 150 is_save=True, 151 info='Vertex coordinate vectors of points. with format [[[x11,y11,z11],[x12,y12,z12]],[[x21,y21,z21],[x22,y22,z122]],...]', 152 )) 153 154 self.add_col(ArrayConf('ids_sumo', '', 155 dtype='object', 156 perm='r', 157 is_index=True, 158 name='ID SUMO', 159 info='Edge ID of SUMO network', 160 )) 161 162 163class Selection(ArrayObjman): 164 def __init__(self, ident, parent=None, **kwargs): 165 self._init_objman(ident, parent=parent, **kwargs) 166 self.add_col(TabIdsArrayConf('obj_ids', 167 name='Object[id]', 168 info='Draw obj and ids', 169 )) 170 171 172class Collection(ArrayObjman): 173 def __init__(self, ident, parent=None, **kwargs): 174 self._init_objman(ident, parent=parent, **kwargs) 175 self.add_col(TabIdListArrayConf('tab_id_lists', 176 name='[Tab1[id1],Tab2[id1],...]', 177 info='Collection of different items from different tables.', 178 )) 179 180 181class TestClass(BaseObjman): 182 def __init__(self, ident='testobj', parent=None, name='Test Object'): 183 self._init_objman(ident, parent=parent, name=name, xmltag=ident) 184 attrsman = self.set_attrsman(Attrsman(self)) 185 186 self.netfilepath = attrsman.add(AttrConf('netfilepath', 'pathtofile', 187 groupnames=['state'], 188 perm='rw', 189 is_save=True, 190 name='Network file', 191 metatype='filepath', 192 info='Network file path', 193 xmltag='netfilepath', 194 )) 195 196 self.workdirpath = attrsman.add(AttrConf('workdirpath', 'pathtodir', 197 groupnames=['state'], 198 perm='rw', 199 is_save=True, 200 name='Workdir', 201 metatype='dirpath', 202 info='Working directory.', 203 xmltag='workdirpath', 204 )) 205 206 self.access = attrsman.add(AttrConf('access', ['bus', 'bike', 'tram'], 207 groupnames=['state'], 208 perm='rw', 209 is_save=True, 210 name='Access list', 211 info='List with vehicle classes that have access', 212 xmltag='access', 213 )) 214 215 self.allowed = attrsman.add(AttrConf('allowed', 0, choices={'bus': 0, 'bike': 1, 'tram': 2}, 216 groupnames=['state'], 217 perm='rw', 218 is_save=True, 219 name='Access list', 220 info='List with vehicle classes that have access', 221 xmltag='allowed', 222 )) 223 224 self.emissiontype = attrsman.add(AttrConf('emissiontype', 'Euro 0', 225 groupnames=['state'], 226 perm='rw', 227 is_save=True, 228 name='Emission type', 229 info='Emission type of vehicle', 230 xmltag='emissiontype', 231 )) 232 233 self.x = attrsman.add(NumConf('x', 1.0, 234 digits_integer=None, digits_fraction=4, 235 minval=0.0, maxval=None, 236 groupnames=['state'], 237 perm='rw', 238 is_save=True, 239 unit='m', 240 is_plugin=True, 241 name='position', 242 info='Test object x position', 243 xmltag='x', 244 )) 245 246 self.x_thresh = attrsman.add(NumConf('x_thresh', 0.0, 247 digits_integer=None, digits_fraction=4, 248 minval=0.0, maxval=None, 249 groupnames=['state'], 250 perm='rw', 251 is_save=True, 252 unit='m', 253 is_plugin=True, 254 name='position threshold', 255 info='Test object x position threshold', 256 xmltag='x_thesh', 257 )) 258 259 self.is_pos_ok = attrsman.add(FuncConf('is_pos_ok', 'on_is_pos_ok', False, 260 groupnames=['state'], 261 name='Pos OK', 262 info='True if position greater than thhreshold.', 263 )) 264 265 attrsman.print_attrs() 266 267 def on_is_pos_ok(self): 268 """ 269 True if position greater than thhreshold. 270 """ 271 print 'on_is_pos_ok', self.x > self.x_thresh 272 return self.x > self.x_thresh 273 274 275class TestClass3(BaseObjman): 276 def __init__(self, ident='testobj3', parent=None, name='Test Object3'): 277 self._init_objman(ident=ident, parent=parent, name=name, xmltag='testobj3') 278 attrsman = self.set_attrsman(Attrsman(self)) 279 280 self.child1 = attrsman.add(ObjConf(parent.child1, is_child=False)) 281 282 self.y = attrsman.add(AttrConf('y', 0.0, 283 groupnames=['state'], 284 perm='r', 285 is_save=True, 286 unit='m', 287 metatype='length', 288 is_plugin=True, 289 name='position', 290 info='Test object y position', 291 )) 292 293 294class TestClass2(BaseObjman): 295 def __init__(self, ident='testobj2', parent=None, name='Test Object2', xmltag='testobj2'): 296 self._init_objman(ident, parent=parent, name=name, xmltag=xmltag) 297 attrsman = self.set_attrsman(Attrsman(self)) 298 299 self.child1 = attrsman.add(ObjConf(TestClass('child1', self)) 300 ) 301 302 print 'TestClass2.child1', self.child1 303 304 self.child3 = attrsman.add(ObjConf(TestClass3('child3', self)) 305 ) 306 307 308class TestTabman(BaseObjman): 309 def __init__(self, ident='test_tabman', parent=None, name='Test Table manage'): 310 self._init_objman(ident, parent=parent, name=name) 311 tm = Tabman(obj=self) 312 self.set_attrsman(tm) 313 self.surname = attrsman.add_col(ColConf('surname', 'xx', 314 groupnames=['state'], 315 perm='rw', 316 is_save=True, 317 name='Family name', 318 info='Name of Family', 319 )) 320 321 self.streetname = attrsman.add_col(ColConf('streetname', 'via della...', 322 groupnames=['state'], 323 perm='rw', 324 is_save=False, 325 name='Street name', 326 info='Name of the street', 327 )) 328 329 #_id = attrsman.suggest_id() 330 # print '_id =',_id 331 # self.attrman.add(_id) 332 333 # print 'self.streetname',self.streetname,type(self.streetname) 334 # self.streetname[1]='yyy' 335 # print 'self.streetname',self.streetname,type(self.streetname) 336 attrsman.add_rows(5) 337 attrsman.streetname[3] = 'ssss' 338 attrsman.streetname[[1, 2]] = ['aa', 55] 339 print 'test get', attrsman.streetname[[1, 2]] 340 # self.streetname[1]+='zzz' 341 attrsman.del_rows([1, 3]) 342 attrsman.del_row(5) 343 # attrsman.delete('streetname') 344 345 346class TestTableObjMan(TableObjman): 347 def __init__(self, ident='test_tableobjman_simple', parent=None, name='Test Table Object Manager'): 348 self._init_objman(ident, parent=parent, name=name, xmltag=('testtab', 'row', None)) 349 350 # ATTENTION!! 351 # do not use x = self.add(...) or self.add_col(...) 352 # This would overwrite the configuration with the value 353 # because the attribute is the configuration, which is set by Attrman 354 # While the add method is returning the value 355 self.add(AttrConf('x', 0.0, 356 groupnames=['state'], 357 perm='r', 358 is_save=True, 359 unit='m', 360 metatype='length', 361 is_plugin=False, 362 name='position', 363 info='Test object x position', 364 xmltag='pos', 365 )) 366 367 self.add(AttrConf('is_pos_ok', False, 368 groupnames=['state'], 369 perm='rw', 370 is_save=True, 371 name='Pos OK', 372 info='True if position is OK', 373 xmltag='pos_ok', 374 )) 375 376 self.add_col(ColConf('surname', 'xx', 377 groupnames=['state'], 378 perm='r', 379 is_save=True, 380 name='Family name', 381 info='Name of Family', 382 xmltag='surname', 383 )) 384 385 self.add_col(ColConf('streetname', 'via della...', 386 groupnames=['state'], 387 perm='rw', 388 is_save=True, 389 name='Street name', 390 info='Name of the street', 391 xmltag='streetname', 392 )) 393 394 fruits = ['allpes', 'bananas', 'oranges'] 395 self.add_col(ColConf('fruits', fruits[0], 396 groupnames=['state'], 397 choices=fruits, 398 perm='rw', 399 is_save=False, 400 name='Fruits', 401 info='Choose a fruit.', 402 )) 403 404 self.add_col(NumcolConf('distances', 0.0, 405 digits_integer=None, digits_fraction=4, 406 minval=0.0, maxval=None, 407 groupnames=['state'], 408 perm='rw', 409 is_save=True, 410 name='Distance', 411 unit='m', 412 info='Distance of the street', 413 xmltag='distances', 414 )) 415 416 self.add(FuncConf('new_row', 'on_new_row', None, 417 groupnames=['rowfunctions', '_private'], 418 name='New row', 419 info='Add a new row.', 420 )) 421 self.add(FuncConf('delete_row', 'on_del_row', None, 422 groupnames=['rowfunctions', '_private'], 423 name='Del row', 424 #info = 'Delete a row.', 425 )) 426 427 #_id = attrsman.suggest_id() 428 # print '_id =',_id 429 # self.attrman.add(_id) 430 431 # print 'self.streetname',self.streetname,type(self.streetname) 432 # self.streetname[1]='yyy' 433 # print 'self.streetname',self.streetname,type(self.streetname) 434 self.add_rows(5) 435 self.streetname[3] = 'ssss' 436 self.surname[[1, 2, 3, 4]] = ['a', 'bb', 'ccc', 'dddd'] 437 self.streetname[[1, 2]] = ['vv', 'dd'] 438 # print '\n\ntest get',self.streetname[[1,2,3]] 439 # self.streetname[1]+='zzz' 440 # self.del_rows([1,3]) 441 # self.del_row(5) 442 # self.delete('streetname') 443 # self.delete('is_pos_ok') 444 # print 'dir',dir(self) 445 446 def on_new_row(self, ids): 447 """ 448 True if position greater than thhreshold. 449 """ 450 self.add_row() 451 452 def on_del_row(self, id_row): 453 """ 454 True if position greater than thhreshold. 455 """ 456 print 'on_del_row', id_row 457 self.del_row(id_row) 458 print ' ids after del', self.get_ids() 459 460 461class TestTableObjManNocols(TableObjman): 462 """ 463 Table manager without columns...for test purposes 464 """ 465 466 def __init__(self, ident='test_tableobjman_simple_nocols', parent=None, name='Test Table Object Manager'): 467 self._init_objman(ident, parent=parent, name=name) 468 469 # ATTENTION!! 470 # do not use x = self.add(...) or c=self.add_col(...) 471 # This would overwrite the configuration with the value 472 # because the attribute is the configuration, which is set by Attrman 473 # While the add method is returning the value 474 self.add(AttrConf('x', 0.0, 475 groupnames=['state'], 476 perm='r', 477 is_save=True, 478 unit='m', 479 metatype='length', 480 is_plugin=False, 481 name='position', 482 info='Test object x position', 483 )) 484 485 self.add(AttrConf('is_pos_ok', False, 486 groupnames=['state'], 487 perm='rw', 488 is_save=True, 489 name='Pos OK', 490 info='True if position is OK', 491 )) 492 493 494class ZonesTab(ArrayObjman): 495 def __init__(self, ident, parent=None, **kwargs): 496 self._init_objman(ident, parent=parent, **kwargs) 497 498 self.add_col(ColConf('zonetypes', 0, 499 choices={ 500 "priority": 0, 501 "traffic_light": 1, 502 "right_before_left": 2, 503 "unregulated": 3, 504 "priority_stop": 4, 505 "traffic_light_unregulated": 5, 506 "allway_stop": 6, 507 "rail_signal": 7, 508 "zipper": 8, 509 "traffic_light_right_on_red": 9, 510 "rail_crossing": 10, 511 }, 512 is_plugin=True, 513 #dtype = np.int32, 514 perm='rw', 515 #is_index = True, 516 name='Type', 517 info='Zone type.', 518 )) 519 520 self.add_col(NumcolConf('shapes', [], 521 groupnames=['state'], 522 perm='rw', 523 is_plugin=True, 524 is_save=True, 525 name='Shape', 526 info='Shape of zone which is a list of (x,y) coordinates', 527 )) 528 529 530class OdTripsTab(ArrayObjman): 531 def __init__(self, ident, parent, zones, **kwargs): 532 self._init_objman(ident, parent=parent, **kwargs) 533 534 self.add_col(IdsArrayConf('ids_orig', zones, 535 groupnames=['state'], 536 is_save=True, 537 name='ID Orig', 538 info='ID of traffic assignment zone of origin of trip.', 539 )) 540 541 self.add_col(IdsConf('ids_dest', zones, 542 groupnames=['state'], 543 is_save=True, 544 name='ID Dest', 545 info='ID of traffic assignment zone of destination of trip.', 546 )) 547 548 self.add_col(NumArrayConf('tripnumbers', 0, 549 groupnames=['state'], 550 perm='rw', 551 is_save=True, 552 name='Trip number', 553 info='Number of trips from zone with ID Orig to zone with ID Dest.', 554 )) 555 556 557class OdModesTab(ArrayObjman): 558 def __init__(self, ident, parent=None, **kwargs): 559 self._init_objman(ident, parent=parent, **kwargs) 560 561 self.add_col(ObjsConf('odtrips', 562 groupnames=['state'], 563 is_save=True, 564 name='OD matrix', 565 info='Matrix with trips from origin to destintion', 566 )) 567 568 569class OdIntervalsTab(ArrayObjman): 570 def __init__(self, ident, parent=None, **kwargs): 571 self._init_objman(ident, parent=parent, **kwargs) 572 573 self.add_col(NumArrayConf('t_start', 0.0, 574 groupnames=['state'], 575 perm='rw', 576 is_save=True, 577 name='Start time', 578 unit='s', 579 info='Start time of interval', 580 )) 581 582 self.add_col(NumArrayConf('t_end', 3600.0, 583 groupnames=['state'], 584 perm='rw', 585 is_save=True, 586 name='End time', 587 unit='s', 588 info='End time of interval', 589 )) 590 591 self.add_col(ObjsConf('odmodes', 592 groupnames=['state'], 593 is_save=True, 594 name='Modes', 595 info='Transport mode', 596 )) 597 598########################################################################### 599# Instance creation 600 601 602demand = BaseObjman('demand') 603 604zones = ZonesTab('zones', parent=demand) 605demand.zones = demand.get_attrsman().add(ObjConf(zones)) 606EVTDELITEM = 20 # delete attribute 607EVTSETITEM = 21 # set attribute 608EVTGETITEM = 22 # get attribute 609EVTADDITEM = 23 # add/create attribute 610zones.shapes.plugin.add_event(EVTADDITEM, on_event_additem) 611shapes = [[(0.0, 10.0), (10.0, 10.0), (10.0, 0.0)], 612 [(10.0, 20.0), (20.0, 20.0), (20.0, 10.0)], 613 [(20.0, 30.0), (30.0, 20.0), (30.0, 20.0)], 614 ] 615zones.add_rows(3, shapes=shapes) 616 617odintervals = OdIntervalsTab('odintervals', parent=demand, info='OD data for different time intervals') 618demand.odintervals = demand.get_attrsman().add(ObjConf(odintervals)) 619odintervals.add_rows(2, t_start=[0, 3600], t_end=[3600, 7200]) 620for id_odmodes in odintervals.get_ids(): 621 odmodes = OdModesTab((odintervals.odmodes.attrname, id_odmodes), parent=odintervals) 622 odintervals.odmodes[id_odmodes] = odmodes 623 624 odmodes.add_rows(2) 625 for id_odtrips in odmodes.get_ids(): 626 odtrips = OdTripsTab((odmodes.odtrips.attrname, id_odtrips), odmodes, zones) 627 odtrips.add_rows(3, ids_orig=[3, 2, 1], ids_dest=[3, 3, 3], tripnumbers=[10, 200, 555]) 628 odmodes.odtrips[id_odtrips] = odtrips 629 630# demand.attrsman.print_attrs() 631# odintervals.print_attrs() 632 633# ------------------------------------------------------------------------------- 634 635 636# vertices = [ [0.0,10.0,10.0,10.0,10.0,0.0], 637# [10.0,20.0,20.0,20.0,20.0,10.0], 638# [20.0,30.0,30.0,20.0,30.0,20.0], 639# ] 640 641# vertices = [ [(0.0,10.0),(10.0,10.0)], 642# [(10.0,20.0),(20.0,20.0)], 643# [(20.0,30.0),(30.0,20.0)], 644# ] 645vertices = [ 646 [[0.0, 0.0, 0.0], [0.2, 0.0, 0.0]], # 0 647 [[0.3, 0.0, 0.0], [0.9, 0.0, 0.0]], # 1 648 [[0.5, 0.0, 0.1], [1.9, 0.0, 0.0]], # 2 649] 650polygons = [ 651 np.array([[0.0, 0.0, 0.0], [0.2, 0.0, 0.0], [0.2, 0.0, 0.1], [0.3, 0.3, 0.3]]), # 0 652 np.array([[0.3, 0.0, 0.0], [0.9, 0.0, 0.0]]), # 1 653 np.array([[0.5, 0.0, 0.1], [1.9, 0.0, 0.0], [0.2, 0.2, 0.2]]), # 2 654] 655ids_sumo = ['aa10', 'bb22', 'cc333'] 656# lines.add_rows(3) 657drawing = BaseObjman('drawing') 658 659lines = Lines('lines', parent=drawing) 660drawing.lines = drawing.get_attrsman().add(ObjConf(lines)) 661lines.add_rows(3, vertices=vertices, polygons=polygons, ids_sumo=ids_sumo) 662 663triangles = Lines('triangles', parent=drawing) 664drawing.triangles = drawing.get_attrsman().add(ObjConf(triangles)) 665triangles.add_rows(3, vertices=2*vertices, polygons=polygons, ids_sumo=['xxx10', 'xx22', 'xx333']) 666 667selection = Selection('selection', parent=drawing) 668drawing.selection = drawing.get_attrsman().add(ObjConf(selection)) 669selection.add_rows(2, obj_ids=[(lines, 2), (triangles, 1)]) 670 671collections = Collection('collections', parent=drawing) 672drawing.collections = drawing.get_attrsman().add(ObjConf(collections)) 673collections.add_rows(2, tab_id_lists=[[(lines, 2), (triangles, 1)], 674 [(lines, 2), (triangles, 1), (lines, 1)]] 675 ) 676 677# ------------------------------------------------------------------------------- 678 679pointvertices = [ 680 [0.0, 0.0, 0.0], 681 [0.2, 0.0, 0.0], 682 [0.3, 0.5, 0.0], 683 [0.2, 0.5, 0.0], 684 [0.0, 0.5, 0.1], 685 [-1.5, -0.5, 0.0], 686] 687 688pointvertices2 = [ 689 [0.0, 0.3, 0.0], 690 [0.2, 0.3, 0.0], 691 [0.3, 0.8, 0.0], 692 [0.2, 0.8, 0.0], 693 [0.0, 0.8, 0.1], 694 [-1.5, -0.8, 0.0], 695] 696 697pointvertices3 = [ 698 [0.5, 0.3, 0.0], 699 [-1.5, -0.8, 0.0], 700] 701 702 703polylines = Polylines() 704polylines.draw(pointvertices, 'aa10&1') 705polylines.draw(pointvertices2, 'bb2210&1') 706polylines.draw(pointvertices3, '5b2310&1') 707polylines.print_attrs() 708xm.write_obj_to_xml(polylines, 'test_polylines.xml') 709