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 landuse.py 12# @author Joerg Schweizer 13# @date 14# @version $Id$ 15 16import os 17import sys 18import time 19import shutil 20from xml.sax import saxutils, parse, handler 21if __name__ == '__main__': 22 try: 23 APPDIR = os.path.dirname(os.path.abspath(__file__)) 24 except: 25 APPDIR = os.path.dirname(os.path.abspath(sys.argv[0])) 26 SUMOPYDIR = os.path.join(APPDIR, '..', '..') 27 sys.path.append(SUMOPYDIR) 28 29 30import numpy as np 31from numpy import random 32from collections import OrderedDict 33from coremodules.modules_common import * 34import agilepy.lib_base.classman as cm 35import agilepy.lib_base.arrayman as am 36import agilepy.lib_base.xmlman as xm 37from agilepy.lib_base.geometry import * 38from agilepy.lib_base.processes import Process, CmlMixin 39from coremodules.network.network import SumoIdsConf, MODES 40import maps 41 42 43def clean_osm(filepath_in, filepath_out): 44 """ 45 Clean osm file from strange characters that compromize importing. 46 """ 47 # 48 substitutes = {""": "'", "&": "+"} 49 fd_in = open(filepath_in, 'r') 50 fd_out = open(filepath_out, 'w') 51 for line in fd_in.readlines(): 52 for oldstr, newstr in substitutes.iteritems(): 53 line = line.replace(oldstr, newstr) 54 fd_out.write(line) 55 fd_in.close() 56 fd_out.close() 57 58 59class LanduseTypes(am.ArrayObjman): 60 def __init__(self, parent, is_add_default=True, **kwargs): 61 62 self._init_objman(ident='landusetypes', 63 parent=parent, 64 name='Landuse types', 65 info='Table with information on landuse types', 66 **kwargs) 67 68 self._init_attributes() 69 if is_add_default: 70 self.add_types_default() 71 72 def _init_attributes(self): 73 # landuse types table 74 self.add_col(am.ArrayConf('typekeys', '', 75 is_index=True, 76 dtype='object', # actually a string of variable length 77 perm='r', 78 name='Type', 79 info='Type of facility. Must be unique, because used as key.', 80 )) 81 82 # self.add_col(am.ArrayConf( 'osmid', '', 83 # dtype = 'object',# actually a string of variable length 84 # perm='rw', 85 # name = 'Name', 86 # info = 'Name of facility type used as reference in OSM.', 87 # )) 88 89 self.add_col(am.ListArrayConf('osmfilters', 90 perm='r', 91 name='OSM filter', 92 info='List of openstreetmap filters that allow to identify this facility type.', 93 )) 94 95 self.add_col(am.ArrayConf('colors', np.ones(4, np.float32), 96 dtype=np.float32, 97 metatype='color', 98 perm='rw', 99 name='Colors', 100 info="Color corrispondig to landuse type as RGBA tuple with values from 0.0 to 1.0", 101 xmltag='color', 102 )) 103 104 self.add_col(am.ArrayConf('descriptions', '', 105 dtype='object', # actually a string of variable length 106 perm='r', 107 name='Info', 108 info='Information about this landuse.', 109 )) 110 111 if len(self) > 0: 112 self.clear() 113 self.add_types_default() 114 115 def format_ids(self, ids): 116 return ','.join(self.typekeys[ids]) 117 118 def get_id_from_formatted(self, idstr): 119 return self.typekeys.get_id_from_index(idstr) 120 121 def get_ids_from_formatted(self, idstrs): 122 return self.typekeys.get_ids_from_indices_save(idstrs.split(',')) 123 124 def add_types_default(self): 125 # default types 126 self.add_row(typekeys='leisure', 127 descriptions='Areas which offer leasure type activities', 128 osmfilters=['sport', 'leisure.park', 'park'], 129 colors=(0.2, 0.5, 0.3, 0.7) 130 ) 131 self.add_row(typekeys='commercial', 132 descriptions='Areas with trade, offices, banks, shopping opportunitties, etc.', 133 osmfilters=['shop.*', 'building.commercial'], 134 colors=(0.6171875, 0.6171875, 0.875, 0.7), 135 ) 136 self.add_row(typekeys='industrial', 137 descriptions='Areas with industrial production facilities.', 138 osmfilters=['building.industrial'], 139 colors=(0.89453125, 0.65625, 0.63671875, 0.7), 140 ) 141 self.add_row(typekeys='parking', 142 descriptions='Areas reserved for car parking.', 143 osmfilters=['building.parking', 'amenity.parking'], 144 colors=(0.52734375, 0.875, 0.875, 0.7), 145 ) 146 self.add_row(typekeys='residential', 147 descriptions='Residential Areas', 148 osmfilters=['building.*', 'building'], 149 colors=(0.921875, 0.78125, 0.4375, 0.7), 150 ) 151 self.add_row(typekeys='mixed', 152 descriptions='Areas with mixed land use, which cannot be clearly assigned to one of the other landuse types.', 153 osmfilters=[], 154 colors=(0.5, 0.9, 0.5, 0.7), 155 ) 156 self.add_row(typekeys='sink', 157 descriptions='Areas where vehicles disappear (evaporate). These zones are used for turn-flow demand models in order to avoid the creation of routes with loops.', 158 osmfilters=[], 159 colors=(0.5, 0.0, 0.1, 1.0), 160 ) 161 self.add_row(typekeys='education', 162 descriptions='Educational facilities such as schools, universities', 163 osmfilters=['building.scool', 'building.university'], 164 colors=(0.921875, 0.78125, 0.4375, 0.7), 165 ) 166 167 168class Zones(am.ArrayObjman): 169 def __init__(self, parent, edges, **kwargs): 170 self._init_objman(ident='zones', parent=parent, 171 name='Zones', 172 info='Traffic Zones which can be used for zone-to-zone traffic transport demand or to specify zones for traffic evaporation.', 173 is_plugin=True, 174 version=0.1, 175 **kwargs) 176 177 self._init_attributes() 178 179 def _init_attributes(self): 180 # print 'Zones._init_attributes',hasattr(self,'are_evaporate') 181 edges = self.parent.get_net().edges 182 self.add_col(SumoIdsConf('Zone', name='Name', perm='rw')) 183 184 self.add_col(am.IdsArrayConf('ids_landusetype', self.parent.landusetypes, 185 id_default=6, 186 #choices = self.parent.landusetypes.typekeys.get_indexmap(), 187 #choiceattrname = 'typekeys', 188 groupnames=['state'], 189 perm='rw', 190 name='Type', 191 info='Zone type. This is actually the landuse type.', 192 )) 193 194 self.add_col(am.ArrayConf('coords', np.zeros(3, dtype=np.float32), 195 groupnames=['state'], 196 perm='r', 197 name='Coords', 198 unit='m', 199 info='Zone center coordinates.', 200 is_plugin=True, 201 )) 202 203 self.add_col(am.ListArrayConf('shapes', 204 groupnames=['_private'], 205 perm='rw', 206 name='Shape', 207 unit='m', 208 info='List of 3D Shape coordinates delimiting a zone.', 209 is_plugin=True, 210 )) 211 212 self.add_col(am.IdlistsArrayConf('ids_edges_orig', edges, 213 groupnames=['state'], 214 name='IDs orig edges', 215 info='List with IDs of network edges that can be used as origins for trips in this zone.', 216 )) 217 218 self.add_col(am.IdlistsArrayConf('ids_edges_dest', edges, 219 groupnames=['state'], 220 name='IDs dest edges', 221 info='List with IDs of network edges that can be used as origins for trips in this zone.', 222 )) 223 224 self.add_col(am.ListArrayConf('probs_edges_orig', 225 groupnames=['_private'], 226 # perm='rw', 227 name='edge probs origin', 228 info='Probabilities of edges to be at the origin of a trip departing from this zone.', 229 )) 230 self.add_col(am.ListArrayConf('probs_edges_dest', 231 groupnames=['_private'], 232 # perm='rw', 233 name='edge probs dest', 234 info='Probabilities of edges to be a destination of a trip arriving at this zone.', 235 )) 236 237 def make(self, zonename='', 238 coord=np.zeros(3, dtype=np.float32), 239 shape=[], 240 id_landusetype=-1): 241 """ 242 Add a zone 243 """ 244 # print 'Zone.make',coord 245 # print ' shape',type(shape),shape 246 247 self.get_coords_from_shape(shape) 248 id_zone = self.add_row(coords=self.get_coords_from_shape(shape), 249 shapes=shape, 250 ids_landusetype=id_landusetype, 251 ) 252 if zonename == '': 253 self.ids_sumo[id_zone] = str(id_zone) 254 else: 255 self.ids_sumo[id_zone] = zonename 256 257 self.identify_zoneedges(id_zone) 258 # print ' shapes\n',self.shapes.value 259 # print ' zone.shapes[id_zone]\n',self.shapes[id_zone] 260 261 return id_zone 262 263 def format_ids(self, ids): 264 return ','.join(self.ids_sumo[ids]) 265 266 def get_id_from_formatted(self, idstr): 267 return self.ids_sumo.get_id_from_index(idstr) 268 269 def get_ids_from_formatted(self, idstrs): 270 return self.ids_sumo.get_ids_from_indices_save(idstrs.split(',')) 271 272 def get_coords_from_shape(self, shape): 273 # print 'get_coords_from_shape',np.array(shape),np.mean(np.array(shape),0) 274 return np.mean(np.array(shape), 0) 275 276 def del_element(self, id_zone): 277 # print 'del_element',id_zone 278 self.del_row(id_zone) 279 280 def get_edges(self): 281 return self.ids_edges_dest.get_linktab() 282 283 def refresh_zoneedges(self): 284 for _id in self.get_ids(): 285 self.identify_zoneedges(_id) 286 self.make_egdeprobs(_id) 287 288 def update_netoffset(self, deltaoffset): 289 """ 290 Called when network offset has changed. 291 Children may need to adjust theur coordinates. 292 """ 293 # self.zones.update_netoffset(deltaoffset) 294 self.coords.value[:, :2] = self.coords.value[:, :2] + deltaoffset 295 shapes = self.shapes.value 296 for i in xrange(len(shapes)): 297 s = np.array(shapes[i]) 298 s[:, :2] = s[:, :2] + deltaoffset 299 shapes[i] = list(s) 300 301 def identify_zoneedges(self, id_zone): 302 # print 'identify_zoneedges',id_zone 303 inds_within = [] 304 ind = 0 305 # print ' self.shapes[id_zone]',self.shapes[id_zone] 306 307 polygon = np.array(self.shapes[id_zone])[:, :2] 308 for polyline in self.get_edges().shapes.value: 309 # print ' polygon',polygon,type(polygon) 310 # print ' np.array(polyline)[:,:2]',np.array(polyline)[:,:2],type(np.array(polyline)[:,:2]) 311 if is_polyline_in_polygon(np.array(polyline)[:, :2], polygon): 312 inds_within.append(ind) 313 ind += 1 314 315 # print ' inds_within',inds_within 316 317 # select and determine weights 318 self.ids_edges_orig[id_zone] = self.get_edges().get_ids(inds_within) 319 self.ids_edges_dest[id_zone] = self.get_edges().get_ids(inds_within) 320 321 def make_egdeprobs(self, id_zone): 322 """ 323 Returns two dictionaries with normalized edge weight distribution 324 one for departures and one for arrivals. 325 326 The dictionaries have id_zone as key and a and an array of edge weights as value. 327 """ 328 #zones = self.zones.value 329 #edgeweights_orig = {} 330 #edgeweights_dest = {} 331 332 # for id_zone in zones.get_ids(): 333 n_edges_orig = len(self.ids_edges_orig[id_zone]) 334 n_edges_dest = len(self.ids_edges_dest[id_zone]) 335 # da fare meglio... 336 if n_edges_orig > 0: 337 self.probs_edges_orig[id_zone] = 1.0/float(n_edges_orig)*np.ones(n_edges_orig, np.float) 338 else: 339 self.probs_edges_orig[id_zone] = 1.0 340 341 if n_edges_dest > 0: 342 self.probs_edges_dest[id_zone] = 1.0/float(n_edges_dest)*np.ones(n_edges_dest, np.float) 343 else: 344 self.probs_edges_dest[id_zone] = 1.0 345 346 def export_evaporator_xml(self, filepath=None, encoding='UTF-8'): 347 """ 348 Export trips to SUMO xml file. 349 Method takes care of sorting trips by departure time. 350 """ 351 if filepath is None: 352 filepath = self.get_tripfilepath() 353 print 'export_trips_xml', filepath 354 try: 355 fd = open(filepath, 'w') 356 except: 357 print 'WARNING in write_obj_to_xml: could not open', filepath 358 return False 359 360 fd.write('<?xml version="1.0" encoding="%s"?>\n' % encoding) 361 fd.write(xm.begin('???')) 362 indent = 2 363 364 for id_zone in self.select_ids(self.are_evaporate.value): 365 366 inds_selected[ids_vtype == id_vtype] = False 367 ids_trip_selected = ids_trip[inds_selected] 368 ids_vtype_selected = set(ids_vtype[inds_selected]) 369 #ids_vtypes_selected = set(ids_vtypes).difference(ids_vtypes_exclude) 370 371 self.parent.vtypes.write_xml(fd, indent=indent, 372 ids=ids_vtype_selected, 373 is_print_begin_end=False) 374 375 self.write_xml(fd, indent=indent, 376 ids=ids_trip_selected, 377 attrconfigs_excluded=[self.routes, self.ids_routes], 378 is_print_begin_end=False) 379 380 fd.write(xm.end(xmltag)) 381 fd.close() 382 return filepath 383 384 385class FacilityTypeMixin(cm.BaseObjman): 386 def __init__(self, ident, parent, 387 name='Facility Type Mixin', 388 info='Provides methods to handle specific facility functions.', 389 **kwargs): 390 """ 391 To be overridden. 392 """ 393 # attention parent is the Strategies table 394 self._init_objman(ident, parent, **kwargs) 395 attrsman = self.set_attrsman(cm.Attrsman(self)) 396 self._init_attributes() 397 self._init_constants() 398 399 def _init_attributes(self): 400 self._init_attributes_common() 401 402 def _init_attributes_common(self): 403 # print 'StrategyMixin._init_attributes' 404 attrsman = self.get_attrsman() 405 landusetypes = self.get_landuse().landusetypes 406 self.ids_landusetype = attrsman.add(cm.AttrConf('ids_landusetype', 407 [landusetypes.get_id_from_formatted('residential'), ], 408 groupnames=['parameters'], 409 perm='rw', 410 #choices = landusetypes.typekeys.get_indexmap(), 411 name='landuse', 412 info='Default landuse type of this facility.', 413 )) 414 415 self.osmkey = attrsman.add(cm.AttrConf('osmkey', 'building.yes', 416 groupnames=['parameters'], 417 perm='rw', 418 name='OSM key', 419 info='Default Open Street Map key for this facility.', 420 )) 421 422 self.length_min = attrsman.add(cm.AttrConf('length_min', 15.0, 423 groupnames=['parameters'], 424 perm='rw', 425 name='Min. length', 426 unit='m', 427 info='Minimum length of entire property.', 428 )) 429 430 self.length_max = attrsman.add(cm.AttrConf('length_max', 100.0, 431 groupnames=['parameters'], 432 perm='rw', 433 name='Max. length', 434 unit='m', 435 info='Maximum length of entire property.', 436 )) 437 self.width_min = attrsman.add(cm.AttrConf('width_min', 20.0, 438 groupnames=['parameters'], 439 perm='rw', 440 name='Min. width', 441 unit='m', 442 info='Minimum width of entire property.', 443 )) 444 445 self.width_max = attrsman.add(cm.AttrConf('width_max', 80.0, 446 groupnames=['parameters'], 447 perm='rw', 448 name='Max. width', 449 unit='m', 450 info='Maximum width of entire property.', 451 )) 452 453 self.height_min = attrsman.add(cm.AttrConf('height_min', 15.0, 454 groupnames=['parameters'], 455 perm='rw', 456 name='Min. height', 457 unit='m', 458 info='Minimum height of facility.', 459 )) 460 461 self.height_max = attrsman.add(cm.AttrConf('height_max', 35.0, 462 groupnames=['parameters'], 463 perm='rw', 464 name='Max. height', 465 unit='m', 466 info='Maximum height of facility.', 467 )) 468 469 self.dist_road_min = attrsman.add(cm.AttrConf('dist_road_min', 1.0, 470 groupnames=['parameters'], 471 perm='rw', 472 name='Min. dist road', 473 unit='m', 474 info='Minimum distance from road.', 475 )) 476 477 self.dist_road_max = attrsman.add(cm.AttrConf('dist_road_max', 5.0, 478 groupnames=['parameters'], 479 perm='rw', 480 name='Max. dist road', 481 info='Maximum distance from road.', 482 )) 483 484 self.dist_prop_min = attrsman.add(cm.AttrConf('dist_prop_min', 1.0, 485 groupnames=['parameters'], 486 perm='rw', 487 name='Min. prop dist', 488 unit='m', 489 info='Minimum distance to other properties.', 490 )) 491 492 self.dist_prop_max = attrsman.add(cm.AttrConf('dist_prop_max', 4.0, 493 groupnames=['parameters'], 494 perm='rw', 495 name='Max. prop dist', 496 info='Maximum distance to other properties.', 497 )) 498 499 self.shape_default = attrsman.add(cm.AttrConf('shape_default', [[0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [1.0, 1.0, 0.0], [0.0, 1.0, 0.0]], 500 groupnames=['parameters'], 501 perm='r', 502 name='Defaut shape', 503 info='Basic facility shape.', 504 )) 505 506 self.unitvolume_default = attrsman.add(cm.AttrConf('unitvolume_default', 507 default=100.0, 508 dtype=np.float32, 509 perm='r', 510 name='Unit volume', 511 info='Default value of the volume necessary to store one person or container. Volume used to calculate capacity.', 512 )) 513 514 def _init_constants(self): 515 516 # self.get_attrsman().do_not_save_attrs([ 517 # '_id_mode_bike','_id_mode_auto','_id_mode_moto', 518 # ]) 519 pass 520 521 def get_id_type(self): 522 # print 'get_id_type from ',self.parent.get_ident_abs() 523 # print ' names',self.parent.names.get_value() 524 return self.parent.names.get_id_from_index(self.get_ident()) 525 526 def get_facilities(self): 527 return self.parent.parent 528 529 def get_landuse(self): 530 return self.parent.parent.parent 531 532 def get_scenario(self): 533 return self.parent.parent.get_scenario() 534 535 def get_shape0(self, length_fac, width_fac, height_max, capacity): 536 """ 537 Returns facility shape in origin coordinate system 538 and height as a function of the capacity and available space 539 """ 540 # One could do some random operations on the default shape 541 # here just stretch default shape to fit area 542 shape = np.array(self.shape_default, dtype=np.float32) * [length_fac, width_fac, 0.0] 543 # y-axes must be flipped so that hoses grow 544 # to the right side of the road 545 shape[:, 1] *= -1 546 # Calculates height of the building in function of the 547 # required capacity and available space. 548 549 # here just use a random number 550 # to be overridden 551 height = min(random.uniform(self.height_min, self.height_max), height_max) 552 553 return shape, height 554 555 def generate(self, offset=[0.0, 0.0, 0.0], 556 length=10.0, 557 width=10.0, 558 angle=0.0, 559 pos_edge=0.0, 560 capacity=None, 561 id_landusetype=None, 562 height_max=30.0, 563 id_edge=None, 564 width_edge=3.0, 565 ): 566 n_shape = len(self.shape_default) 567 shape_fac = np.zeros((n_shape, 3), dtype=np.float32) 568 569 # determine effecive land area 570 dist_edge = random.uniform(self.dist_road_min, self.dist_road_max) + width_edge 571 width_fac = width-dist_edge 572 573 dist_prop = random.uniform(self.dist_prop_min, self.dist_prop_max) 574 length_fac = length-2*self.dist_prop_max 575 576 # do offset 577 dxn = np.cos(angle-np.pi/2) 578 dyn = np.sin(angle-np.pi/2) 579 580 offset_fac = offset\ 581 + np.array([dxn, dyn, 0.0], dtype=np.float32)*dist_edge\ 582 + np.array([dyn, dxn, 0.0], dtype=np.float32)*dist_prop 583 584 shape, height_fac = self.get_shape0(length_fac, width_fac, height_max, capacity) 585 586 # transform in to the right place 587 shape_fac[:, 0] = shape[:, 0]*np.cos(angle) - shape[:, 1]*np.sin(angle) 588 shape_fac[:, 1] = shape[:, 0]*np.sin(angle) + shape[:, 1]*np.cos(angle) 589 shape_fac += offset_fac 590 591 if id_landusetype is None: 592 id_landusetype = self.ids_landusetype[0] 593 594 id_fac = self.get_facilities().make(id_landusetype=id_landusetype, 595 id_zone=None, 596 id_facilitytype=self.get_id_type(), 597 osmkey=self.osmkey, 598 area=None, 599 height=height_fac, 600 centroid=None, 601 shape=list(shape_fac), 602 id_roadedge_closest=id_edge, 603 position_roadedge_closest=pos_edge + 0.5 * length, 604 ) 605 606 607class FacilityTypeHouse(FacilityTypeMixin): 608 def __init__(self, ident, parent, 609 name='Single House', 610 info='Parameters and methods for residential house.', 611 **kwargs): 612 613 self._init_objman(ident, parent, name=name, info=info, **kwargs) 614 attrsman = self.set_attrsman(cm.Attrsman(self)) 615 616 self._init_attributes() 617 self._init_constants() 618 # specific init 619 620 621class FacilityTypes(am.ArrayObjman): 622 def __init__(self, ident, facilities, is_add_default=True, **kwargs): 623 self._init_objman(ident=ident, 624 parent=facilities, 625 name='Facility types', 626 info='Table holding facility type specific parameters and an object with methods ', 627 **kwargs) 628 629 self._init_attributes() 630 if is_add_default: 631 self.add_default() 632 633 def _init_attributes(self): 634 # landuse types table 635 self.add_col(am.ArrayConf('names', 636 default='', 637 dtype='object', 638 perm='r', 639 is_index=True, 640 name='Short name', 641 info='Strategy name. Must be unique, used as index.', 642 )) 643 644 self.add_col(am.ArrayConf('unitvolumes', 645 default=100.0, 646 dtype=np.float32, 647 perm='rw', 648 name='Unit volume', 649 info='The volume necessary to store one person or container. Volume used to calculate capacity.', 650 )) 651 652 self.add_col(cm.ObjsConf('typeobjects', 653 #groupnames = ['state'], 654 name='Type objects', 655 info='Facility type object.', 656 )) 657 658 self.add_default() 659 660 def format_ids(self, ids): 661 return ','.join(self.names[ids]) 662 663 def get_id_from_formatted(self, idstr): 664 return self.names.get_id_from_index(idstr) 665 666 def get_ids_from_formatted(self, idstrs): 667 return self.names.get_ids_from_indices_save(idstrs.split(',')) 668 669 def add_default(self): 670 self.clear() 671 self.add_type('house', FacilityTypeHouse) 672 673 def get_typeobj(self, id_type): 674 return self.typeobjects[id_type] 675 676 def add_type(self, ident, TypeClass, **kwargs): 677 # print 'add_strategy', ident 678 if not self.names.has_index(ident): 679 factypeobj = TypeClass(ident, self) 680 id_type = self.add_row(names=ident, 681 typeobjects=factypeobj, 682 unitvolumes=factypeobj.unitvolume_default 683 ) 684 return id_type 685 else: 686 return self.get_id_from_formatted(id_type) 687 688 689class Facilities(am.ArrayObjman): 690 def __init__(self, landuse, landusetypes, zones, net=None, **kwargs): 691 # print 'Facilities.__init__',hasattr(self,'lanes') 692 self._init_objman(ident='facilities', 693 parent=landuse, 694 name='Facilities', 695 info='Information on buildings, their type of usage and access to the transport network.', 696 xmltag=('polys', 'poly', 'ids_sumo'), 697 is_plugin=True, 698 **kwargs) 699 700 self._init_attributes() 701 702 if net is not None: 703 self.add_col(am.IdsArrayConf('ids_roadedge_closest', net.edges, 704 groupnames=['landuse'], 705 name='Road edge ID', 706 info='ID of road edge which is closest to this facility.', 707 )) 708 709 self.add_col(am.ArrayConf('positions_roadedge_closest', 0.0, 710 dtype=np.float32, 711 groupnames=['landuse'], 712 perm='r', 713 name='Road edge pos', 714 unit='m', 715 info='Position on road edge which is closest to this facility', 716 )) 717 718 # self.ids_stop_closest = self.facilities.add(cm.ArrayConf( 'ids_stop_closest', None, 719 # dtype = 'object', 720 # name = 'ID stops', 721 # perm='rw', 722 # info = 'List of IDs of closest public transport stops.', 723 # )) 724 725 def _init_attributes(self): 726 landusetypes = self.parent.landusetypes 727 zones = self.parent.zones 728 self.add(cm.ObjConf(FacilityTypes('facilitytypes', self,))) 729 730 self.add_col(SumoIdsConf('Facility', info='SUMO facility ID')) 731 732 self.add_col(am.IdsArrayConf('ids_landusetype', landusetypes, 733 groupnames=['landuse'], 734 perm='rw', 735 name='ID landuse', 736 info='ID of landuse.', 737 is_plugin=True, 738 )) 739 740 self.add_col(am.IdsArrayConf('ids_zone', zones, 741 groupnames=['landuse'], 742 perm='r', 743 name='ID zone', 744 info='ID of traffic zone, where this facility is located.', 745 )) 746 facilitytypes = self.get_facilitytypes() 747 if len(facilitytypes) > 0: 748 id_default = facilitytypes.get_ids()[0] 749 else: 750 id_default = -1 751 self.add_col(am.IdsArrayConf('ids_facilitytype', facilitytypes, 752 id_default=id_default, 753 groupnames=['landuse'], 754 perm='rw', 755 name='ID fac. type', 756 info='ID of facility type (house, scycraper, factory, parking,...).', 757 is_plugin=True, 758 )) 759 760 self.add_col(am.ArrayConf('osmkeys', 'building.yes', 761 dtype='object', # actually a string of variable length 762 perm='rw', 763 name='OSM key', 764 info='OSM key of facility.', 765 xmltag='type', 766 is_plugin=True, 767 )) 768 769 self.add_col(am.ArrayConf('capacities', 0, 770 dtype=np.int32, 771 groupnames=['landuse'], 772 perm='r', 773 name='Capacity', 774 info='Person capacity of this facility. For example maximum number of adulds living in a building or number of people working in a factory.', 775 )) 776 777 self.add_col(am.ArrayConf('areas', 0.0, 778 dtype=np.float32, 779 groupnames=['landuse'], 780 perm='r', 781 name='Area', 782 unit='m^2', 783 info='Area of this facility.', 784 )) 785 786 self.add_col(am.ArrayConf('heights', 10.0, 787 dtype=np.float32, 788 groupnames=['landuse'], 789 perm='r', 790 name='Height', 791 unit='m', 792 info='Height above ground of this facility.', 793 )) 794 795 self.add_col(am.ArrayConf('centroids', np.zeros(3, dtype=np.float32), 796 dtype=np.float32, 797 groupnames=['state', '_private'], 798 perm='r', 799 name='Center', 800 unit='m', 801 info='Center coordinates of this facility.', 802 )) 803 804 self.add_col(am.ListArrayConf('shapes', 805 groupnames=['_private'], 806 perm='rw', 807 name='Shape', 808 unit='m', 809 info='List of 3D Shape coordinates of facility.', 810 xmltag='shape', 811 is_plugin=True, 812 )) 813 814 # self.add_col(TabIdsArrayConf( 'ftypes', 815 # name = 'Types', 816 # info = 'Draw obj and ids', 817 # )) 818 819 if self.plugin is None: 820 self.init_plugin(True) 821 self.shapes.init_plugin(True) 822 self.osmkeys.init_plugin(True) 823 self.ids_landusetype.init_plugin(True) 824 # configure only if net is initialized 825 826 def make(self, id_sumo=None, 827 id_landusetype=None, 828 id_zone=None, 829 id_facilitytype=None, 830 osmkey=None, 831 area=None, 832 capacity=None, 833 height=None, 834 centroid=None, 835 shape=[], 836 id_roadedge_closest=None, 837 position_roadedge_closest=None, 838 ): 839 """ 840 Adds a facilities 841 """ 842 id_fac = self.suggest_id() 843 if id_sumo is None: 844 id_sumo = str(id_fac) 845 846 # stuff with landusetype must be done later 847 id_fac = self.add_row(_id=id_fac, 848 ids_sumo=id_sumo, 849 ids_landusetype=id_landusetype, 850 ids_zone=id_zone, 851 ids_facilitytype=id_facilitytype, 852 osmkeys=osmkey, 853 areas=area, 854 capacities=capacity, 855 heights=height, 856 centroids=centroid, 857 shapes=shape, 858 ids_roadedge_closest=id_roadedge_closest, 859 positions_roadedge_closest=position_roadedge_closest, 860 ) 861 # do area calcs and other 862 if area is None: 863 self.update_area(id_fac) 864 865 if capacity is None: 866 self.update_capacity(id_fac) 867 868 if centroid is None: 869 self.update_centroid(id_fac) 870 return id_fac 871 872 def generate(self, facilitytype, **kwargs): 873 """ 874 Generates a facility. The generation of the facility will be 875 performed by the faciliy type instance. 876 """ 877 return facilitytype.generate(**kwargs) 878 879 def format_ids(self, ids): 880 return ','.join(self.ids_sumo[ids]) 881 882 def get_id_from_formatted(self, idstr): 883 return self.ids_sumo.get_id_from_index(idstr) 884 885 def get_ids_from_formatted(self, idstrs): 886 return self.ids_sumo.get_ids_from_indices_save(idstrs.split(',')) 887 888 def del_element(self, id_fac): 889 # print 'del_element',id_zone 890 self.del_row(id_fac) 891 892 def write_xml(self, fd, indent=0, is_print_begin_end=True): 893 xmltag, xmltag_item, attrname_id = self.xmltag 894 layer_default = -1 895 fill_default = 1 896 ids_landusetype = self.ids_landusetype 897 landusecolors = self.get_landusetypes().colors 898 899 if is_print_begin_end: 900 fd.write(xm.begin(xmltag, indent)) 901 902 attrsconfigs_write = [self.ids_sumo, self.osmkeys, self.shapes] 903 for _id in self.get_ids(): 904 fd.write(xm.start(xmltag_item, indent+2)) 905 for attrsconfig in attrsconfigs_write: 906 attrsconfig.write_xml(fd, _id) 907 908 landusecolors.write_xml(fd, ids_landusetype[_id]) 909 fd.write(xm.num('layer', layer_default)) 910 fd.write(xm.num('fill', fill_default)) 911 912 fd.write(xm.stopit()) 913 914 if is_print_begin_end: 915 fd.write(xm.end(xmltag, indent)) 916 917 def get_landusetypes(self): 918 return self.ids_landusetype.get_linktab() 919 920 def get_facilitytypes(self): 921 return self.facilitytypes.get_value() 922 923 def get_net(self): 924 # print 'get_net',self.ids_edge_closest_road.get_linktab(),self.ids_edge_closest_road.get_linktab().parent 925 return self.ids_roadedge_closest.get_linktab().parent 926 927 def get_scenario(self): 928 return self.ids_roadedge_closest.get_linktab().parent.parent 929 930 def update_netoffset(self, deltaoffset): 931 """ 932 Called when network offset has changed. 933 Children may need to adjust theur coordinates. 934 """ 935 # self.zones.update_netoffset(deltaoffset) 936 self.centroids.value[:, :2] = self.centroids.value[:, :2] + deltaoffset 937 shapes = self.shapes.value 938 for i in xrange(len(shapes)): 939 s = np.array(shapes[i]) 940 s[:, :2] = s[:, :2] + deltaoffset 941 shapes[i] = list(s) 942 943 def get_edges(self): 944 return self.ids_roadedge_closest.get_linktab() 945 946 def identify_taz(self): 947 """ 948 Identifies id of traffic assignment zone for each facility. 949 Note that not all facilities are within such a zone. 950 """ 951 zones = self.ids_zone.get_linktab() 952 # self.get_demand().get_districts() 953 for id_fac in self.get_ids(): 954 for id_zone in zones.get_ids(): 955 if is_polyline_in_polygon(self.shapes[id_fac], zones.shapes[id_zone]): 956 self.ids_zone[id_fac] = id_zone 957 break 958 959 def get_departure_probabilities(self): 960 """ 961 Returns a dictionary, where probabilities[id_zone] 962 is a vector of departure probabilities for each facility 963 of zone id_zone. 964 """ 965 zones = self.ids_zone.get_linktab() 966 # print 'get_departure_probabilities in n_zones',len(zones) 967 probabilities = {} 968 969 inds_fac = self.get_inds() 970 for id_zone in zones.get_ids(): 971 # probabilities[id_zone]={} 972 # for id_landusetype in set(self.ids_landusetype.value): 973 # print ' id_zone',id_zone 974 # print ' ids_landusetype',self.ids_landusetype.value[inds_fac] 975 # print ' ids_zone',self.ids_zone.value[inds_fac] 976 # print '' 977 util = self.capacities.value[inds_fac].astype(np.float32)*(self.ids_zone.value[inds_fac] == id_zone) 978 util_tot = np.sum(util) 979 # print '\n\n [id_taz][ftype]',id_taz,ftype,util_tot,np.sum(util/np.sum(util)) 980 # print ' self.type==ftype',self.type==ftype 981 # print ' self.id_taz==id_taz',self.id_taz==id_taz 982 # print ' util',util 983 if util_tot > 0.0: 984 probabilities[id_zone] = util/util_tot 985 else: 986 probabilities[id_zone] = util # all zero prob 987 988 return probabilities, self.get_ids(inds_fac) 989 990 def get_departure_probabilities_landuse(self): 991 """ 992 Returns the dictionnary of dictionaries with departure (or arrival) 993 probabilities where probabilities[id_zone][id_landusetype] 994 is a probability distribution vector giving for each facility the 995 probability to depart/arrive in zone id_zone with facility type ftype. 996 997 The ids_fac is an array that contains the facility ids in correspondence 998 to the probability vector. 999 """ 1000 print 'get_departure_probabilities_landuse' 1001 probabilities = {} 1002 zones = self.ids_zone.get_linktab() 1003 inds_fac = self.get_inds() 1004 for id_zone in zones.get_ids(): 1005 probabilities[id_zone] = {} 1006 for id_landusetype in set(self.ids_landusetype.value): 1007 print ' id_zone,id_landusetype', id_zone, id_landusetype 1008 # print ' ids_landusetype',self.ids_landusetype.value[inds_fac] 1009 # print ' ids_zone',self.ids_zone.value[inds_fac] 1010 # print '' 1011 util = self.capacities.value[inds_fac].astype( 1012 np.float32)*((self.ids_landusetype.value[inds_fac] == id_landusetype) & (self.ids_zone.value[inds_fac] == id_zone)) 1013 util_tot = np.sum(util) 1014 # print '\n\n [id_taz][ftype]',id_taz,ftype,util_tot,np.sum(util/np.sum(util)) 1015 # print ' self.type==ftype',self.type==ftype 1016 # print ' self.id_taz==id_taz',self.id_taz==id_taz 1017 # print ' util',util 1018 if util_tot > 0.0: 1019 probabilities[id_zone][id_landusetype] = util/util_tot 1020 else: 1021 probabilities[id_zone][id_landusetype] = util # all zero prob 1022 1023 return probabilities, self.get_ids(inds_fac) 1024 1025 def update(self, ids=None): 1026 # print 'update',ids 1027 if ids is None: 1028 ids = self.get_ids() 1029 1030 for _id in ids: 1031 # print ' self.centroids[_id]',self.centroids[_id] 1032 # print ' self.shapes[_id]',self.shapes[_id],np.mean(self.shapes[_id],0) 1033 self.update_centroid(_id) 1034 #self.areas[_id] = find_area(np.array(self.shapes[_id],float)[:,:2]) 1035 self.update_area(_id) 1036 #self.areas[_id] = get_polygonarea_fast(np.array(self.shapes[_id],float)[:,0], np.array(self.shapes[_id],float)[:,1]) 1037 1038 self.update_capacities(ids) 1039 # self.identify_closest_edge(ids) 1040 1041 def update_centroid(self, _id): 1042 self.centroids[_id] = np.mean(self.shapes[_id], 0) 1043 1044 def update_area(self, _id): 1045 self.areas[_id] = get_polygonarea_fast(np.array(self.shapes[_id], float)[ 1046 :, 0], np.array(self.shapes[_id], float)[:, 1]) 1047 1048 def update_capacity(self, id_fac): 1049 self.update_capacities([id_fac]) 1050 1051 def update_capacities(self, ids): 1052 volumes_unit = self.get_facilitytypes().unitvolumes[self.ids_facilitytype[ids]] 1053 self.capacities[ids] = self.areas[ids]*self.heights[ids]/volumes_unit 1054 1055 def get_dists(self, ids_fac_from, ids_fac_to): 1056 """ 1057 Returns centroid to centroid distance from facilities in vector 1058 ids_fac_from to facilities in vector ids_fac_to. 1059 """ 1060 1061 return np.sqrt(np.sum((self.centroids[ids_fac_to]-self.centroids[ids_fac_from])**2)) 1062 1063 def identify_closest_edge(self, ids=None, priority_max=5, has_sidewalk=True): 1064 """ 1065 Identifies edge ID and position on this edge that 1066 is closest to the centoid of each facility and the satisfies certain 1067 conditions. 1068 """ 1069 print 'identify_closest_edge' 1070 edges = self.get_edges() 1071 1072 # select edges...if (edges.priorities[id_edge]<=priority_max) & edges.has_sidewalk(id_edge): 1073 1074 ids_edge = edges.select_ids((edges.priorities.get_value() < priority_max) 1075 & (edges.widths_sidewalk.get_value() > 0.0)) 1076 1077 edges.make_segment_edge_map(ids_edge) 1078 1079 if ids is None: 1080 ids = self.get_ids() 1081 for id_fac in ids: 1082 id_edge = edges.get_closest_edge(self.centroids[id_fac]) 1083 1084 # determin position on edeg where edge is closest to centroid 1085 # TODO: solve this faster with precalculated maps!! 1086 xc, yc, zc = self.centroids[id_fac] 1087 shape = edges.shapes[id_edge] 1088 n_segs = len(shape) 1089 1090 d_min = 10.0**8 1091 x_min = 0.0 1092 y_min = 0.0 1093 j_min = 0 1094 p_min = 0.0 1095 pos = 0.0 1096 x1, y1, z1 = shape[0] 1097 edgelength = edges.lengths[id_edge] 1098 for j in xrange(1, n_segs): 1099 x2, y2, z2 = shape[j] 1100 d, xp, yp = shortest_dist(x1, y1, x2, y2, xc, yc) 1101 # print ' x1,y1=(%d,%d)'%(x1,y1),',x2,y2=(%d,%d)'%(x2,y2),',xc,yc=(%d,%d)'%(xc,yc) 1102 # print ' d,x,y=(%d,%d,%d)'%shortest_dist(x1,y1, x2,y2, xc,yc) 1103 if d < d_min: 1104 d_min = d 1105 # print ' **d_min=',d_min,[xp,yp] 1106 x_min = xp 1107 y_min = yp 1108 j_min = j 1109 p_min = pos 1110 # print ' pos',pos,[x2-x1,y2-y1],'p_min',p_min 1111 pos += np.linalg.norm([x2-x1, y2-y1]) 1112 x1, y1 = x2, y2 1113 1114 x1, y1, z1 = shape[j_min-1] 1115 pos_min = p_min+np.linalg.norm([x_min-x1, y_min-y1]) 1116 # print ' k=%d,d_min=%d, x1,y1=(%d,%d),xmin,ymin=(%d,%d),xc,yc=(%d,%d)'%(k,d_min,x1,y1,x_min,y_min,xc,yc) 1117 # print ' pos=%d,p_min=%d,pos_min=%d'%(pos,p_min,pos_min) 1118 1119 if pos_min > edgelength: 1120 pos_min = edgelength 1121 1122 if pos_min < 0: 1123 pos_min = 0 1124 # print ' id_fac,id_edge',id_fac,id_edge,pos_min 1125 self.ids_roadedge_closest[id_fac] = id_edge 1126 self.positions_roadedge_closest[id_fac] = pos_min 1127 1128 def set_shape(self, id_fac, shape): 1129 # print 'set_shape',id_fac,shape 1130 self.shapes[id_fac] = shape 1131 self.update([id_fac]) 1132 #self.areas[id_fac] = find_area(shape[:,:2]) 1133 #self.centroids[id_fac] =np.mean(shape,0) 1134 1135 def add_polys(self, ids_sumo=[], **kwargs): 1136 """ 1137 Adds a facilities as used on sumo poly xml info 1138 """ 1139 # stuff with landusetype must be done later 1140 return self.add_rows(n=len(ids_sumo), ids_sumo=ids_sumo, **kwargs) 1141 1142 def add_poly(self, id_sumo, id_landusetype=None, osmkey=None, shape=np.array([], np.float32)): 1143 """ 1144 Adds a facility as used on sumo poly xml info 1145 """ 1146 # print 'add_poly',id_sumo,id_landusetype,osmkey 1147 1148 landusetypes = self.get_landusetypes() 1149 if id_landusetype is not None: 1150 # this means that landusetype has been previousely identified 1151 if osmkey is None: 1152 # use filter as key 1153 osmkey = landusetypes.osmfilters[id_landusetype][0] 1154 1155 id_fac = self.add_row(ids_sumo=id_sumo, 1156 ids_landusetype=id_landusetype, 1157 osmkeys=osmkey, 1158 ) 1159 self.set_shape(id_fac, shape) 1160 return id_fac 1161 1162 else: 1163 # identify ftype from fkeys... 1164 keyvec = osmkey.split('.') 1165 len_keyvec = len(keyvec) 1166 is_match = False 1167 for id_landusetype in landusetypes.get_ids(): 1168 # print ' ',landusetypes.osmfilters[id_landusetype] 1169 # if fkeys==('building.industrial'): print ' check',facilitytype 1170 for osmfilter in landusetypes.osmfilters[id_landusetype]: 1171 # print ' ', 1172 osmfiltervec = osmfilter.split('.') 1173 if osmkey == osmfilter: # exact match of filter 1174 is_match = True 1175 # if fkeys==('building.industrial'):print ' found exact',osmkey 1176 elif (len(osmfiltervec) == 2) & (len_keyvec == 2): 1177 1178 if osmfiltervec[0] == keyvec[0]: 1179 1180 if osmfiltervec[1] == '*': 1181 is_match = True 1182 # if fkeys==('building.industrial'):print ' found match',osmkeyvec[0] 1183 1184 # redundent to exact match 1185 # elif osmkeyvec[1]==keyvec[1]: 1186 # is_match = True 1187 # if is_match: 1188 # print ' found exact',osmkey 1189 1190 if is_match: 1191 # if fkeys==('building.industrial'):print ' *found:',facilitytype,fkeys 1192 # return self.facilities.set_row(ident, type = facilitytype, polygon=polygon, fkeys = fkeys,area=find_area(polygon),centroid=np.mean(polygon,0)) 1193 id_fac = self.add_row(ids_sumo=id_sumo, 1194 ids_landusetype=id_landusetype, 1195 osmkeys=osmkey, 1196 ) 1197 self.set_shape(id_fac, shape) 1198 return id_fac 1199 1200 def clear(self): 1201 # self.reset() 1202 self.clear_rows() 1203 1204 def set_shapes(self, ids, vertices): 1205 self.shapes[ids] = vertices 1206 if not hasattr(ids, '__iter__'): 1207 ids = [ids] 1208 self.update(ids) 1209 1210 def import_poly(self, polyfilepath, is_remove_xmlfiles=False): 1211 print 'import_poly from %s ' % (polyfilepath,) 1212 self.clear() 1213 # let's read first the offset information, which are in the 1214 # comment area 1215 fd = open(polyfilepath, 'r') 1216 is_comment = False 1217 is_processing = False 1218 offset = self.get_net().get_offset() # default is offset from net 1219 # print ' offset,offset_delta',offset,type(offset) 1220 #offset = np.array([0,0],float) 1221 for line in fd.readlines(): 1222 if line.find('<!--') >= 0: 1223 is_comment = True 1224 if is_comment & (line.find('<processing') >= 0): 1225 is_processing = True 1226 if is_processing & (line.find('<offset.x') >= 0): 1227 offset[0] = float(xm.read_keyvalue(line, 'value')) 1228 elif is_processing & (line.find('<offset.y') >= 0): 1229 offset[1] = float(xm.read_keyvalue(line, 'value')) 1230 break 1231 fd.close() 1232 offset_delta = offset - self.get_net().get_offset() 1233 1234 exectime_start = time.clock() 1235 1236 counter = SumoPolyCounter() 1237 parse(polyfilepath, counter) 1238 fastreader = SumoPolyReader(self, counter, offset_delta) 1239 parse(polyfilepath, fastreader) 1240 fastreader.finish() 1241 1242 # update ids_landuse... 1243 # self.update() 1244 1245 # timeit 1246 print ' exec time=', time.clock() - exectime_start 1247 1248 # print ' self.shapes',self.shapes.value 1249 1250 1251class SumoPolyCounter(handler.ContentHandler): 1252 """Counts facilities from poly.xml file into facility structure""" 1253 1254 def __init__(self): 1255 self.n_fac = 0 1256 1257 def startElement(self, name, attrs): 1258 # print 'startElement',name,len(attrs) 1259 if name == 'poly': 1260 self.n_fac += 1 1261 1262 1263class SumoPolyReader(handler.ContentHandler): 1264 """Reads facilities from poly.xml file into facility structure""" 1265 1266 def __init__(self, facilities, counter, offset_delta): 1267 1268 self._facilities = facilities 1269 self._ids_landusetype_all = self._facilities.get_landusetypes().get_ids() 1270 self._osmfilters = self._facilities.get_landusetypes().osmfilters 1271 1272 self._ind_fac = -1 1273 self.ids_sumo = np.zeros(counter.n_fac, np.object) 1274 self.ids_landusetype = -1*np.ones(counter.n_fac, np.int32) 1275 self.osmkeys = np.zeros(counter.n_fac, np.object) 1276 self.shape = np.zeros(counter.n_fac, np.object) 1277 self.areas = np.zeros(counter.n_fac, np.float32) 1278 self.centroids = np.zeros((counter.n_fac, 3), np.float32) 1279 1280 #self._id_facility = None 1281 self._offset_delta = offset_delta 1282 1283 def startElement(self, name, attrs): 1284 1285 # print 'startElement', name, len(attrs) 1286 if name == 'poly': 1287 self._ind_fac += 1 1288 i = self._ind_fac 1289 1290 osmkey = attrs.get('type', 'building.yes') 1291 1292 id_landuse = self.get_landuse(osmkey) 1293 if id_landuse >= 0: # land use is interesting 1294 shape = xm.process_shape(attrs.get('shape', ''), offset=self._offset_delta) 1295 shapearray = np.array(shape, np.float32) 1296 # print ' shapearray',shapearray 1297 self.ids_sumo[i] = attrs['id'] 1298 self.ids_landusetype[i] = id_landuse 1299 self.osmkeys[i] = osmkey 1300 self.shape[i] = shape 1301 self.areas[i] = find_area(shapearray[:, :2]) 1302 self.centroids[i] = np.mean(shapearray, 0) 1303 1304 # color info in this file no longer used as it is defined in 1305 # facility types table 1306 # color = np.array(xm.parse_color(attrs['color']))*0.8,# make em darker!! 1307 1308 def get_landuse(self, osmkey): 1309 keyvec = osmkey.split('.') 1310 len_keyvec = len(keyvec) 1311 # print 'get_landuse',len_keyvec,keyvec 1312 #is_match = False 1313 for id_landusetype in self._ids_landusetype_all: 1314 # print ' ',landusetypes.osmfilters[id_landusetype] 1315 # if fkeys==('building.industrial'): print ' check',facilitytype 1316 for osmfilter in self._osmfilters[id_landusetype]: 1317 osmfiltervec = osmfilter.split('.') 1318 # print ' osmfiltervec',id_landusetype,osmfiltervec,osmkey==osmfilter,(len(osmfiltervec)==2)&(len_keyvec==2) 1319 1320 if osmkey == osmfilter: # exact match of filter 1321 return id_landusetype 1322 1323 elif (len(osmfiltervec) == 2) & (len_keyvec == 2): 1324 1325 if osmfiltervec[0] == keyvec[0]: 1326 1327 if osmfiltervec[1] == '*': 1328 return id_landusetype 1329 return -1 1330 1331 def finish(self): 1332 1333 # print 'write_to_net' 1334 inds_valid = np.flatnonzero(self.ids_landusetype >= 0) 1335 ids_fac = self._facilities.add_polys( 1336 ids_sumo=self.ids_sumo[inds_valid], 1337 ids_landusetype=self.ids_landusetype[inds_valid], 1338 osmkeys=self.osmkeys[inds_valid], 1339 shapes=self.shape[inds_valid], 1340 areas=self.areas[inds_valid], 1341 centroids=self.centroids[inds_valid], 1342 ) 1343 1344 # def characters(self, content): 1345 # if self._id is not None: 1346 # self._currentShape = self._currentShape + content 1347 1348 # def endElement(self, name): 1349 # pass 1350 1351 1352class Parking(am.ArrayObjman): 1353 def __init__(self, landuse, lanes, **kwargs): 1354 # print 'Parking.__init__',lanes,hasattr(self,'lanes') 1355 self._init_objman(ident='parking', parent=landuse, 1356 name='Parking', 1357 info='Information on private car parking.', 1358 #is_plugin = True, 1359 # **kwargs 1360 ) 1361 1362 self._init_attributes(lanes) 1363 1364 def _init_attributes(self, lanes=None): 1365 # print 'Parkin._init_attributes',lanes,hasattr(self,'lanes'),hasattr(self,'ids_lane') 1366 if lanes is None: 1367 # upgrade call 1368 # lanes exists already as link 1369 lanes = self.get_lanes() 1370 1371 # print ' lanes',lanes 1372 # -------------------------------------------------------------------- 1373 # misc params... 1374 1375 # these are options for assignment procedure!! 1376 # self.add(AttrConf( 'length_noparking', kwargs.get('length_noparking',20.0), 1377 # groupnames = ['options'], 1378 # perm='wr', 1379 # unit = 'm', 1380 # name = 'Min Length', 1381 # info = 'Minimum edge length for assigning on-road parking space.' , 1382 # #xmltag = 'pos', 1383 # )) 1384 # 1385 # self.add(AttrConf( 'length_space', kwargs.get('length_space',20.0), 1386 # groupnames = ['options'], 1387 # perm='wr', 1388 # unit = 'm', 1389 # name = 'Lot length', 1390 # info = 'Length of a standard parking lot.' , 1391 # #xmltag = 'pos', 1392 # )) 1393 1394 self.add_col(am.IdsArrayConf('ids_lane', lanes, 1395 name='ID Lane', 1396 info='ID of lane for this parking position. ', 1397 )) 1398 1399 self.add_col(am.ArrayConf('positions', 0.0, 1400 dtype=np.float32, 1401 perm='r', 1402 name='Pos', 1403 unit='m', 1404 info="Position on lane for this parking.", 1405 )) 1406 1407 self.add_col(am.ArrayConf('lengths', 0.0, 1408 dtype=np.float32, 1409 #choices = OPTIONMAP_POS_DEPARTURE, 1410 perm='r', 1411 name='Length', 1412 unit='m', 1413 info="Length of parking lot in edge direction.", 1414 )) 1415 1416 self.add_col(am.ArrayConf('angles', 0.0, 1417 dtype=np.float32, 1418 perm='rw', 1419 name='Angle', 1420 unit='deg', 1421 info="Parking angle with respect to lane direction.", 1422 )) 1423 1424 self.add_col(am.ArrayConf('vertices', np.zeros((2, 3), dtype=np.float32), 1425 dtype=np.float32, 1426 groupnames=['_private'], 1427 perm='r', 1428 name='Coords', 1429 unit='m', 1430 info="Start and end vertices of right side of parking space.", 1431 )) 1432 1433 self.add_col(am.ArrayConf('numbers_booking', 0, # ??? 1434 dtype=np.int32, 1435 perm='r', 1436 name='booked', 1437 info="Number of vehicles booked for this parking.", 1438 )) 1439 1440 # self.add_col(am.ArrayConf( 'durations', 0.0,# ??? 1441 # dtype=np.float32, 1442 # perm='r', 1443 # name = 'Parking duration', 1444 # unit = 's', 1445 # info = "Default duration of car parking.", 1446 # )) 1447 1448 self.add(cm.ObjConf(lanes, is_child=False, groups=['_private'])) 1449 1450 self.add(cm.ObjConf(lanes.parent.edges, is_child=False, groups=['_private'])) 1451 1452 def get_edges(self): 1453 return self.edges.get_value() 1454 1455 def get_lanes(self): 1456 return self.lanes.get_value() 1457 1458 def link_vehiclefleet(self, vehicles): 1459 """ 1460 Links to table with vehicle info. 1461 """ 1462 self.add_col(am.IdsArrayConf('ids_bookedveh', vehicles, 1463 name='ID booked veh', 1464 info='ID of vehicle which has booked this parking position.', 1465 )) 1466 1467 def update_netoffset(self, deltaoffset): 1468 """ 1469 Called when network offset has changed. 1470 Children may need to adjust their coordinates. 1471 """ 1472 pass 1473 1474 def get_parkinglane_from_edge(self, id_edge, id_mode, length_min=15.0, priority_max=8, n_freelanes_min=1): 1475 """ 1476 Check if edge can have on-road parking 1477 """ 1478 edges = self.edges.get_value() 1479 lanes = self.lanes.get_value() 1480 # check size 1481 # laneindex = 1482 # print 'get_parkinglane_from_edge',id_edge, id_mode,priority_max,length_min 1483 # print ' check',(edges.priorities[id_edge]<=priority_max),(edges.lengths[id_edge]>length_min),(edges.widths_sidewalk[id_edge]>-1) 1484 1485 if (edges.priorities[id_edge] <= priority_max) & (edges.lengths[id_edge] > length_min) & (edges.widths_sidewalk[id_edge] > 0): 1486 1487 laneindex = edges.get_laneindex_allowed(id_edge, id_mode) 1488 # print ' found',laneindex,edges.nums_lanes[id_edge]-laneindex > n_freelanes_min 1489 if (laneindex >= 0) & (edges.nums_lanes[id_edge]-laneindex >= n_freelanes_min): 1490 return edges.ids_lanes[id_edge][laneindex] 1491 else: 1492 return -1 1493 1494 return -1 # no parking possible by default 1495 1496 def get_edge_pos_parking(self, id_parking): 1497 lanes = self.lanes.get_value() 1498 return lanes.ids_edge[self.ids_lane[id_parking]], self.positions[id_parking] 1499 1500 # def get_edge_pos_parking(self, id_parking): 1501 # """ 1502 # Retuens edge and position of parking with id_parking 1503 # """ 1504 # ind = self.parking.get_ind(id_parking) 1505 # 1506 # return self.edges.get_value()(self.id_edge_parking[ind]),self.pos_edge_parking[ind] 1507 1508 def make_parking(self, id_mode=MODES['passenger'], length_min=42.0, length_noparking=15.0, length_lot=6.0, angle=0.0, is_clear=True, **kwargs): 1509 print 'make_parking' 1510 if is_clear: 1511 self.clear() 1512 edges = self.edges.get_value() 1513 lanes = self.lanes.get_value() 1514 n_parking = 0 1515 ids_parking = [] 1516 ids_lane_current = self.ids_lane.get_value().copy() 1517 for id_edge in edges.get_ids(): 1518 # check if edge is suitable... 1519 # print ' id_edge,length,n_lanes,',id_edge 1520 id_lane = self.get_parkinglane_from_edge(id_edge, id_mode, length_min, **kwargs) 1521 1522 is_eligible = id_lane >= 0 1523 if not is_clear: 1524 if id_lane not in ids_lane_current: 1525 is_eligible = False 1526 1527 if is_eligible: 1528 n_spaces = int((edges.lengths[id_edge]-2*length_noparking)/length_lot) 1529 # print ' create',id_edge,lanes.indexes[id_lane],edges.lengths[id_edge],n_spaces 1530 # print ' delta',lanes.shapes[id_lane][0]-lanes.shapes[id_lane][-1] 1531 pos_offset = length_noparking 1532 pos = pos_offset 1533 if n_spaces > 0: 1534 for i in xrange(n_spaces): 1535 #id_park = self.suggest_id() 1536 # print ' pos=',pos,pos/edges.lengths[id_edge] 1537 1538 # print ' vertices',get_vec_on_polyline_from_pos(lanes.shapes[id_lane],pos, length_lot, angle = angle) 1539 n_parking += 1 1540 1541 id_park = self.add_row(ids_lane=id_lane, 1542 positions=pos, 1543 lengths=length_lot, 1544 angles=angle, 1545 vertices=get_vec_on_polyline_from_pos( 1546 lanes.shapes[id_lane], pos, length_lot-0.5, angle=angle) 1547 ) 1548 # print ' created id_park,pos', id_park,pos#,get_coord_on_polyline_from_pos(lanes.shapes[id_lane],pos),lanes.shapes[id_lane] 1549 ids_parking.append(id_park) 1550 pos = pos_offset+(i+1)*length_lot 1551 1552 print ' created %d parking spaces' % n_parking 1553 return ids_parking 1554 1555 def clear_booking(self): 1556 self.numbers_booking.reset() 1557 if hasattr(self, 'ids_bookedveh'): 1558 self.ids_bookedveh.reset() 1559 1560 def get_closest_parking(self, id_veh, coord, c_spread=2.0): 1561 """ 1562 Returns parking space for id_veh as close as possible to coord. 1563 """ 1564 1565 #inds_person = self.persons.get_inds(ids_person) 1566 print 'get_closest_parking' 1567 ind_parking_closest = self.get_inds()[np.argmin( 1568 np.sum((coord-self.vertices.value[:, 1, :])**2, 1) + c_spread*lengths*self.lengths.get_value())] 1569 self.numbers_booking.get_value()[ind_parking_closest] += 1 1570 return self.get_ids(ind_parking_closest), ind_parking_closest 1571 1572 def get_closest_parkings(self, ids_veh, coords, c_spread=2.0): 1573 """ 1574 Returns parking space for each vehicle in ids_veh as close as possible to coords. 1575 """ 1576 1577 #inds_person = self.persons.get_inds(ids_person) 1578 n = len(ids_veh) 1579 # print 'get_closest_parking',n,len(self) 1580 if len(self) == 0: 1581 print 'WARNING in get_closest_parkings: there is no parking.' 1582 return [], [] 1583 1584 #parking = self.get_landuse().parking 1585 #inds_parking = parking.get_inds() 1586 coord_parking = self.vertices.value[:, 1, :] 1587 # print ' coord_parking',coord_parking 1588 numbers_booking = self.numbers_booking.get_value() 1589 lengths = self.lengths.get_value() 1590 inds_vehparking = np.zeros(n, int) 1591 1592 #inds_parking_avail = np.flatnonzero( self.ids_bookedveh.value == -1).tolist() 1593 inds_parking_avail = self.get_inds().copy() 1594 #ids_veh = np.zeros(n,object) 1595 i = 0 1596 for id_veh, coord in zip(ids_veh, coords): 1597 # print '\n id_veh',id_veh 1598 # print ' landuse.id_bookedveh_parking',landuse.id_bookedveh_parking 1599 # 1600 1601 # print ' inds_parking_avail',inds_parking_avail 1602 # print ' dists',np.sum((coord-coord_parking[inds_parking_avail])**2,1),np.argmin(np.sum((coord-coord_parking[inds_parking_avail])**2,1)) 1603 ind_parking_closest = inds_parking_avail[np.argmin( 1604 np.sum((coord-coord_parking)**2, 1) + c_spread*lengths*numbers_booking)] 1605 # print ' ind_parking_closest,n_avail',ind_parking_closest,len(inds_parking_avail) 1606 inds_vehparking[i] = ind_parking_closest 1607 # print ' coords_veh',coord 1608 # print ' coord_park',coord_parking[ind_parking_closest] 1609 numbers_booking[ind_parking_closest] += 1 1610 1611 id_parking = self.get_ids([ind_parking_closest]) 1612 id_edge, pos = self.get_edge_pos_parking(id_parking) 1613 # print ' id_veh=%s,id_parking_closest=%s, dist =%.2fm'%(id_veh,id_parking,np.sqrt(np.sum((coord-coord_parking[ind_parking_closest])**2))) 1614 # ids_bookedveh[ind_parking_closest]=id_veh # occupy parking 1615 # print ' id_edge, pos',id_edge, pos 1616 # inds_parking_avail.remove(ind_parking_closest) 1617 i += 1 1618 1619 # print ' inds_vehparking', inds_vehparking 1620 # print ' ids_vehparking', self.get_ids(inds_vehparking) 1621 # print ' ids_veh',ids_veh 1622 #self.ids_bookedveh.value[inds_vehparking] = ids_veh 1623 # self.ids_bookedveh.[ids_parking] =ids_bookedveh 1624 return self.get_ids(inds_vehparking), inds_vehparking 1625 1626 def assign_parking(self, ids_veh, coords, is_overbook=False): 1627 """ 1628 Assigns a parking space to each vehicle as close as possible to coords. 1629 Only one vehicle can be assigned to a parking space. 1630 """ 1631 1632 #inds_person = self.persons.get_inds(ids_person) 1633 n = len(ids_veh) 1634 # print 'assign_parking',n 1635 1636 #parking = self.get_landuse().parking 1637 #inds_parking = parking.get_inds() 1638 coord_parking = self.vertices.value[:, 1, :] 1639 1640 inds_vehparking = np.zeros(n, int) 1641 1642 inds_parking_avail = np.flatnonzero(self.ids_bookedveh.value == -1).tolist() 1643 1644 #ids_veh = np.zeros(n,object) 1645 i = 0 1646 for id_veh, coord in zip(ids_veh, coords): 1647 # print '\n id_veh,coord',id_veh,coord 1648 # print ' landuse.id_bookedveh_parking',landuse.id_bookedveh_parking 1649 # 1650 1651 # print ' inds_parking_avail',inds_parking_avail 1652 # print ' dists',np.sum((coord-coord_parking[inds_parking_avail])**2,1),np.argmin(np.sum((coord-coord_parking[inds_parking_avail])**2,1)) 1653 ind_parking_closest = inds_parking_avail[np.argmin(np.sum((coord-coord_parking[inds_parking_avail])**2, 1))] 1654 # print ' ind_parking_closest,n_avail',ind_parking_closest,len(inds_parking_avail) 1655 inds_vehparking[i] = ind_parking_closest 1656 # print ' id_veh=%s,id_parking_closest=%s, dist =%.2fm'%(id_veh,self.get_ids([ind_parking_closest]),np.sqrt(np.sum((coord-coord_parking[ind_parking_closest])**2))) 1657 # ids_bookedveh[ind_parking_closest]=id_veh # occupy parking 1658 1659 inds_parking_avail.remove(ind_parking_closest) 1660 i += 1 1661 1662 # print ' inds_vehparking', inds_vehparking 1663 # print ' ids_vehparking', self.get_ids(inds_vehparking) 1664 # print ' ids_veh',ids_veh 1665 self.ids_bookedveh.value[inds_vehparking] = ids_veh 1666 # self.ids_bookedveh.[ids_parking] =ids_bookedveh 1667 return self.get_ids(inds_vehparking), inds_vehparking 1668 1669 1670class Landuse(cm.BaseObjman): 1671 def __init__(self, scenario=None, net=None, **kwargs): 1672 self._init_objman(ident='landuse', parent=scenario, name='Landuse', **kwargs) 1673 attrsman = self.set_attrsman(cm.Attrsman(self)) 1674 1675 if scenario is not None: 1676 net = scenario.net 1677 # self.net = attrsman.add( cm.ObjConf( net, is_child = False ) )# link only 1678 1679 self.landusetypes = attrsman.add(cm.ObjConf(LanduseTypes(self))) 1680 self.zones = attrsman.add(cm.ObjConf(Zones(self, net.edges))) 1681 self.facilities = attrsman.add(cm.ObjConf(Facilities(self, self.landusetypes, self.zones, net=net))) 1682 self.parking = attrsman.add(cm.ObjConf(Parking(self, net.lanes))) 1683 self.maps = attrsman.add(cm.ObjConf(maps.Maps(self))) 1684 1685 def update_netoffset(self, deltaoffset): 1686 """ 1687 Called when network offset has changed. 1688 Children may need to adjust theur coordinates. 1689 """ 1690 self.zones.update_netoffset(deltaoffset) 1691 self.facilities.update_netoffset(deltaoffset) 1692 self.parking.update_netoffset(deltaoffset) 1693 self.maps.update_netoffset(deltaoffset) 1694 1695 def get_net(self): 1696 # parent of landuse must be scenario 1697 if self.parent is not None: 1698 return self.parent.net 1699 else: 1700 return None 1701 1702 def export_polyxml(self, filepath=None, encoding='UTF-8'): 1703 """ 1704 Export landuse facilities to SUMO poly.xml file. 1705 """ 1706 if len(self.facilities) == 0: 1707 return None 1708 1709 if filepath is None: 1710 if self.parent is not None: 1711 filepath = self.get_filepath() 1712 else: 1713 filepath = os.path.join(os.getcwd(), 'landuse.poly.xml') 1714 1715 print 'export_polyxml', filepath 1716 try: 1717 fd = open(filepath, 'w') 1718 except: 1719 print 'WARNING in export_poly_xml: could not open', filepath 1720 return None 1721 1722 #xmltag, xmltag_item, attrname_id = self.xmltag 1723 xmltag_poly = 'additional' 1724 fd.write('<?xml version="1.0" encoding="%s"?>\n' % encoding) 1725 fd.write(xm.begin(xmltag_poly)) 1726 indent = 2 1727 1728 fd.write(xm.start('location', indent+2)) 1729 # print ' groups:',self.parent.net.get_attrsman().get_groups() 1730 for attrconfig in self.parent.net.get_attrsman().get_group('location'): 1731 # print ' locationconfig',attrconfig.attrname 1732 attrconfig.write_xml(fd) 1733 fd.write(xm.stopit()) 1734 1735 self.facilities.write_xml(fd, indent=indent+2, is_print_begin_end=False) 1736 1737 fd.write(xm.end(xmltag_poly)) 1738 fd.close() 1739 return filepath 1740 1741 def get_filepath(self): 1742 return self.parent.get_rootfilepath() + '.poly.xml' 1743 1744 def import_polyxml(self, rootname=None, dirname='', filepath=None): 1745 if filepath is None: 1746 if rootname is not None: 1747 filepath = os.path.join(dirname, rootname+'.poly.xml') 1748 else: 1749 filepath = self.get_filepath() 1750 1751 if os.path.isfile(filepath): 1752 self.facilities.import_poly(filepath) 1753 1754 else: 1755 self.get_logger().w('import_xml: files not found:'+filepath, key='message') 1756 1757 # 1758 # here may be other relevant imports 1759 # 1760 1761 1762class FacilityGenerator(Process): 1763 def __init__(self, ident='facilitygenerator', facilities=None, logger=None, **kwargs): 1764 print 'FacilityGenerator.__init__' 1765 1766 # TODO: let this be independent, link to it or child?? 1767 1768 self._init_common(ident, 1769 parent=facilities, 1770 name='Facility Generator', 1771 logger=logger, 1772 info='Generates facilities (buildigs, factories, parks, etc.) in a given street network.', 1773 ) 1774 1775 attrsman = self.set_attrsman(cm.Attrsman(self)) 1776 1777 # make for each possible pattern a field for prob 1778 1779 self.edgelength_min = attrsman.add(cm.AttrConf('edgelength_min', kwargs.get('edgelength_min', 50.0), 1780 groupnames=['options'], 1781 perm='rw', 1782 name='Minimum edge length', 1783 unit='m', 1784 info="""Minimum edge length for which houses are generated.""", 1785 )) 1786 1787 self.height_max = attrsman.add(cm.AttrConf('height_max', kwargs.get('height_max', 20.0), 1788 groupnames=['options'], 1789 perm='rw', 1790 unit='m', 1791 name='Max facility height', 1792 info="""Maximum height of facilities.""", 1793 )) 1794 1795 self.capacity_max = attrsman.add(cm.AttrConf('capacity_max', kwargs.get('capacity_max', 1000), 1796 groupnames=['options'], 1797 perm='rw', 1798 name='Max. facility capacity', 1799 info="""Maximum capacity of a facility. Capacity is the number of adulds living in a house or working in a factory.""", 1800 )) 1801 1802 self.n_retry = attrsman.add(cm.AttrConf('n_retry', kwargs.get('n_retry', 5), 1803 groupnames=['options'], 1804 perm='rw', 1805 name='Retry number', 1806 info="""Number of times the algorithm is trying to fit a facility in a road-gap.""", 1807 )) 1808 1809 # self.id_facilitytype = attrsman.add(cm.AttrConf( 'id_facilitytype',kwargs.get('id_facilitytype',1), 1810 # groupnames = ['options'], 1811 # perm='rw', 1812 # choices = self.parent.facilities.facilitytypes.get_value().names.get_indexmap(), 1813 # name = 'Facility type', 1814 # info = """Facility type to be generated.""", 1815 # )) 1816 1817 def do(self): 1818 print self.get_name()+'.do' 1819 # links 1820 facilities = self.parent 1821 net = facilities.parent.get_net() 1822 edges = net.edges 1823 nodes = net.nodes 1824 #self._edges = edges 1825 1826 #self._segvertices = edges.get_segvertices_xy() 1827 x1, y1, x2, y2 = edges.get_segvertices_xy() 1828 1829 logger = self.get_logger() 1830 #logger.w('Provide vehicles...') 1831 1832 ids_edge = edges.select_ids((edges.widths_sidewalk.get_value() > 0) 1833 & (edges.lengths.get_value() > self.edgelength_min) 1834 ) 1835 facilitytypes = facilities.facilitytypes.get_value() 1836 1837 # here we can make a selection 1838 facilitytypeobjs = facilitytypes.typeobjects[facilitytypes.get_ids()] 1839 1840 # print ' facilitytypes, facilitytypeobjs',facilitytypes,facilitytypeobjs 1841 n_factypes = len(facilitytypes) 1842 n_fac = 0 1843 # print ' eligible edges =',ids_edge 1844 for id_edge, edgelength, id_fromnode, id_tonode, shape, edgewidth\ 1845 in zip(ids_edge, edges.lengths[ids_edge], 1846 edges.ids_fromnode[ids_edge], 1847 edges.ids_tonode[ids_edge], 1848 edges.shapes[ids_edge], 1849 edges.widths[ids_edge], 1850 ): 1851 pos = 5.0 1852 # print ' Build at edge',id_edge,edgelength 1853 1854 # identify opposite edge, which needs to be excluded 1855 # from bulding overlapping check 1856 if (nodes.ids_incoming[id_fromnode] is not None)\ 1857 & (nodes.ids_outgoing[id_tonode] is not None): 1858 ids_incoming_fomnode = set(nodes.ids_incoming[id_fromnode]) 1859 ids_outgoing_tonode = set(nodes.ids_outgoing[id_tonode]) 1860 1861 id_edge_opp_set = ids_incoming_fomnode.intersection(ids_outgoing_tonode) 1862 if len(id_edge_opp_set) > 0: 1863 id_edge_opp = id_edge_opp_set.pop() 1864 inds_seg_opp = edges.get_inds_seg_from_id_edge(id_edge_opp) 1865 else: 1866 # no edge in opposite direction 1867 id_edge_opp = -1 1868 inds_seg_opp = None 1869 else: 1870 id_edge_opp = -1 1871 inds_seg_opp = None 1872 #ids_tonode_outgoing = edges.ids_tonode[nodes.ids_outgoing[id_tonode]] 1873 # net.get_ids_edge_from_inds_seg(inds_seg) 1874 # net.get_inds_seg_from_id_edge(id_edge) 1875 1876 while pos < edgelength: 1877 facilitytype = facilitytypeobjs[0] # could be according to statistics 1878 # print ' next position',pos 1879 n_trials = self.n_retry 1880 is_success = False 1881 while (n_trials > 0) & (not is_success): 1882 length_fac = random.uniform(facilitytype.length_min, facilitytype.length_max) 1883 width_fac = random.uniform(facilitytype.width_min, facilitytype.width_max) 1884 1885 # fix from to positions 1886 pos11 = pos 1887 pos21 = pos + length_fac 1888 if pos21 < edgelength: 1889 # print ' try place',n_trials,facilitytype,'id_edge',id_edge,pos11,pos21,edgelength 1890 1891 coord11, angle = get_coord_angle_on_polyline_from_pos(shape, pos11) 1892 dxn = np.cos(angle-np.pi/2) 1893 dyn = np.sin(angle-np.pi/2) 1894 coord12 = [coord11[0]+width_fac*dxn, coord11[1]+width_fac*dyn, coord11[2]] 1895 1896 coord21, angle = get_coord_angle_on_polyline_from_pos(shape, pos21) 1897 dxn = np.cos(angle-np.pi/2) 1898 dyn = np.sin(angle-np.pi/2) 1899 coord22 = [coord21[0]+width_fac*dxn, coord21[1]+width_fac*dyn, coord21[2]] 1900 1901 id_edge1 = edges.get_ids_edge_from_inds_seg(self.get_segind_closest_edge( 1902 coord12, x1, y1, x2, y2, inds_seg_exclude=inds_seg_opp)) 1903 1904 #id_edge2 = edges.get_ids_edge_from_inds_seg(self.get_segind_closest_edge(coord22, x1,y1,x2,y2, inds_seg_exclude = inds_seg_opp)) 1905 # print ' id_edge,id_edge1,id_edge2',id_edge,id_edge1,id_edge2 1906 # print ' shape =',np.array([coord11, coord12, coord22, coord21,], dtype = np.float32) 1907 if id_edge1 == id_edge: 1908 id_edge2 = edges.get_ids_edge_from_inds_seg(self.get_segind_closest_edge( 1909 coord22, x1, y1, x2, y2, inds_seg_exclude=inds_seg_opp)) 1910 1911 if id_edge2 == id_edge: 1912 id_fac = facilities.generate(facilitytype, 1913 offset=coord11, # offset 1914 length=length_fac, 1915 width=width_fac, 1916 #bbox = [coord11, coord12, coord22, coord21,], 1917 id_landusetype=None, 1918 angle=angle, 1919 pos_edge=pos11, 1920 capacity=self.capacity_max, # could be function of dist to center/pop 1921 height_max=self.height_max, # could be function of dist to center 1922 id_edge=id_edge, 1923 width_edge=edgewidth, 1924 ) 1925 1926 if id_fac != -1: 1927 # print ' ****generation successful id_fac=',id_fac 1928 is_success = True 1929 n_fac += 1 1930 1931 n_trials -= 1 1932 1933 pos = pos21 1934 # print ' update with pos',pos 1935 # generate a parallel shape with distance width_fac 1936 #angles_perb = get_angles_perpendicular(shape) 1937 #dxn = np.cos(angles_perb) 1938 #dyn = np.sin(angles_perb) 1939 #shape2 = np.zeros(shape.shape, np.float32) 1940 #shape2[:,0] = dxn*width_fac + shape[:,0] 1941 #shape2[:,1] = dyn*width_fac + shape[:,1] 1942 #shape2[:,2] = shape[:,2] 1943 1944 # check if positions on parallel shape are closest to 1945 # this edge or closer to another edge 1946 print ' Done, generated %d facilities' % n_fac 1947 return True 1948 1949 def get_segind_closest_edge(self, p, x1, y1, x2, y2, inds_seg_exclude=None): 1950 d2 = get_dist_point_to_segs(p[0:2], x1, y1, x2, y2, is_ending=True) 1951 if inds_seg_exclude is not None: 1952 d2[inds_seg_exclude] = np.inf 1953 # print ' min(d2)=',np.min(d2),'argmin=',np.argmin(d2),self.get_ids(self._edgeinds[np.argmin(d2)]) 1954 return np.argmin(d2) 1955 1956 1957class ParkingGenerator(Process): 1958 def __init__(self, ident='parkinggenerator', parking=None, logger=None, **kwargs): 1959 print 'ParkingGenerator.__init__' 1960 1961 # TODO: let this be independent, link to it or child?? 1962 1963 self._init_common(ident, 1964 parent=parking, 1965 name='On Road parking generator', 1966 logger=logger, 1967 info='Generates on road parking.', 1968 ) 1969 1970 attrsman = self.set_attrsman(cm.Attrsman(self)) 1971 scenario = parking.parent.parent 1972 1973 self.id_mode = attrsman.add(cm.AttrConf('id_mode', kwargs.get('id_mode', MODES['passenger']), 1974 groupnames=['options'], 1975 choices=scenario.net.modes.names.get_indexmap(), 1976 perm='rw', 1977 name='Mode ID', 1978 info="""Mode of parked vehicles. This is to select lanes which must be accessible for this mode.""", 1979 )) 1980 1981 self.length_min = attrsman.add(cm.AttrConf('length_min', kwargs.get('length_min', 42.0), 1982 groupnames=['options'], 1983 perm='rw', 1984 unit='m', 1985 name='Min. edge length', 1986 info="""Minimum edge length in order to qualify for parking.""", 1987 )) 1988 1989 self.length_noparking = attrsman.add(cm.AttrConf('length_noparking', kwargs.get('length_noparking', 6.0), 1990 groupnames=['options'], 1991 perm='rw', 1992 unit='m', 1993 name='No parking length', 1994 info="""Length from junction to the first or last parking on an edge.""", 1995 )) 1996 1997 self.length_lot = attrsman.add(cm.AttrConf('length_lot', kwargs.get('length_lot', 6.0), 1998 groupnames=['options'], 1999 perm='rw', 2000 unit='m', 2001 name='Lot length', 2002 info="""Length of a single parking lot.""", 2003 )) 2004 self.angle = attrsman.add(cm.AttrConf('angle', kwargs.get('angle', 0.0), 2005 groupnames=['options'], 2006 perm='rw', 2007 name='Angle', 2008 info="""Angle of parking with respect ti lane direction. Currently only 0.0 is possible.""", 2009 )) 2010 2011 self.priority_max = attrsman.add(cm.AttrConf('priority_max', kwargs.get('priority_max', 7), 2012 groupnames=['options'], 2013 perm='rw', 2014 name='Max. priority', 2015 info="""Maximum edge priority where parkings will be created.""", 2016 )) 2017 2018 self.n_freelanes_min = attrsman.add(cm.AttrConf('n_freelanes_min', kwargs.get('n_freelanes_min', 1), 2019 groupnames=['options'], 2020 perm='rw', 2021 name='Min. free lanes', 2022 info="""Minimum number of free lanes on the edge. These is the minimum number of lanes excluding the parking lane.""", 2023 )) 2024 self.is_clear = attrsman.add(cm.AttrConf('is_clear', kwargs.get('is_clear', True), 2025 groupnames=['options'], 2026 perm='rw', 2027 name='Clear', 2028 info="""Clear precious parking areas.""", 2029 )) 2030 2031 def do(self): 2032 print self.get_name()+'.do' 2033 # links 2034 # print ' self.id_mode',self.id_mode 2035 # print ' self.get_kwoptions()',self.get_kwoptions() 2036 self.parent.make_parking(**self.get_kwoptions()) 2037 return True 2038 2039 2040class OsmPolyImporter(CmlMixin, Process): 2041 def __init__(self, landuse=None, 2042 osmfilepaths=None, 2043 typefilepath=None, 2044 polyfilepath=None, 2045 projparams=None, 2046 offset_x=None, 2047 offset_y=None, 2048 is_keep_full_type=True, 2049 is_import_all_attributes=True, 2050 is_use_name_for_id=False, 2051 polytypefilepath='', 2052 is_clean_osmfile=True, 2053 logger=None, **kwargs): 2054 print 'OsmPolyImporter.__init__', landuse, landuse.parent.get_rootfilename() 2055 self._init_common('osmpolyimporter', name='OSM Poly import', 2056 logger=logger, 2057 info='Converts a OSM file to a SUMO Poly file and read facilities into scenario.', 2058 ) 2059 if landuse is None: 2060 self._landuse = Landuse() 2061 else: 2062 self._landuse = landuse 2063 2064 self.init_cml('polyconvert') # pass main shell command 2065 2066 if landuse.parent is not None: 2067 scenario = landuse.parent 2068 rootname = scenario.get_rootfilename() 2069 rootdirpath = scenario.get_workdirpath() 2070 if hasattr(scenario, 'net'): 2071 if projparams is None: 2072 projparams = scenario.net.get_projparams() 2073 if (offset_x is None) & (offset_y is None): 2074 offset_x, offset_y = scenario.net.get_offset() 2075 else: 2076 rootname = landuse.get_ident() 2077 rootdirpath = os.getcwd() 2078 2079 if polyfilepath is None: 2080 polyfilepath = os.path.join(rootdirpath, rootname+'.poly.xml') 2081 2082 if osmfilepaths is None: 2083 osmfilepaths = os.path.join(rootdirpath, rootname+'.osm.xml') 2084 2085 if typefilepath is None: 2086 typefilepath = os.path.join(os.path.dirname(os.path.abspath(__file__)), 2087 '..', '..', 'typemap', 'osmPolyconvert.typ.xml') 2088 attrsman = self.get_attrsman() 2089 2090 self.workdirpath = rootdirpath 2091 2092 self.rootname = rootname 2093 2094 self.add_option('osmfilepaths', osmfilepaths, 2095 groupnames=['options'], 2096 cml='--osm-files', 2097 perm='rw', 2098 name='OSM files', 2099 wildcards='OSM XML files (*.osm)|*.osm*', 2100 metatype='filepaths', 2101 info='Openstreetmap files to be imported.', 2102 ) 2103 2104 self.is_clean_osmfile = attrsman.add(cm.AttrConf('is_clean_osmfile', is_clean_osmfile, 2105 groupnames=['options'], 2106 perm='rw', 2107 name='Clean OSM files', 2108 info='If set, OSM files are cleaned from strange characters prior to import (recommended).', 2109 )) 2110 2111 self.add_option('polyfilepath', polyfilepath, 2112 groupnames=['_private'], 2113 cml='--output-file', 2114 perm='r', 2115 name='Poly file', 2116 wildcards='Poly XML files (*.poly.xml)|*.poly.xml', 2117 metatype='filepath', 2118 info='SUMO Poly file in XML format.', 2119 ) 2120 2121 self.add_option('typefilepath', typefilepath, 2122 groupnames=['options'], 2123 cml='--type-file', 2124 perm='rw', 2125 name='Type file', 2126 wildcards='Typemap XML files (*.typ.xml)|*.typ.xml', 2127 metatype='filepath', 2128 info="""Typemap XML files. In these file, 2129OSM building types are mapped to specific facility parameters, is not explicitely set by OSM attributes.""", 2130 ) 2131 2132 # --net-file <FILE> Loads SUMO-network FILE as reference to offset and projection 2133 self.add_option('projparams', projparams, 2134 groupnames=['options'], 2135 cml='--proj', 2136 perm='rw', 2137 name='projection', 2138 info='Uses STR as proj.4 definition for projection. Default is the projection of the network, better do not touch!', 2139 is_enabled=lambda self: self.projparams is not None, 2140 ) 2141 2142 self.add_option('offset_x', offset_x, 2143 groupnames=['options', 'geometry'], 2144 cml='--offset.x ', 2145 perm='rw', 2146 unit='m', 2147 name='X-Offset', 2148 info='Adds offset to net x-positions; default: 0.0', 2149 is_enabled=lambda self: self.offset_x is not None, 2150 ) 2151 self.add_option('offset_y', offset_y, 2152 groupnames=['options', 'geometry'], 2153 cml='--offset.y ', 2154 perm='rw', 2155 unit='m', 2156 name='Y-Offset', 2157 info='Adds offset to net x-positions; default: 0.0', 2158 is_enabled=lambda self: self.offset_y is not None, 2159 ) 2160 2161 self.add_option('is_keep_full_type', is_keep_full_type, 2162 groupnames=['options'], 2163 cml='--osm.keep-full-type', 2164 perm='rw', 2165 name='keep full OSM type', 2166 info='The type will be made of the key-value - pair.', 2167 ) 2168 2169 self.add_option('is_import_all_attributes', is_keep_full_type, 2170 groupnames=['options'], 2171 cml='--all-attributes', 2172 perm='rw', 2173 name='import all attributes', 2174 info='Imports all OSM attributes.', 2175 ) 2176 2177 self.add_option('is_use_name_for_id', is_use_name_for_id, 2178 groupnames=['options'], 2179 cml='--osm.use-name', 2180 perm='rw', 2181 name='use OSM name for id', 2182 info=' The OSM id (not internal ID) will be set from the given OSM name attribute.', 2183 ) 2184 2185 self.add_option('polytypefilepath', polytypefilepath, 2186 groupnames=[], # ['_private'],# 2187 cml='--type-file', 2188 perm='rw', 2189 name='Poly type file', 2190 wildcards='Net XML files (*.xml)|*.xml', 2191 metatype='filepath', 2192 info='SUMO Poly type file in XML format.', 2193 is_enabled=lambda self: self.polytypefilepath != '', 2194 ) 2195 2196 def update_params(self): 2197 """ 2198 Make all parameters consistent. 2199 example: used by import OSM to calculate/update number of tiles 2200 from process dialog 2201 """ 2202 pass 2203 #self.workdirpath = os.path.dirname(self.netfilepath) 2204 #bn = os.path.basename(self.netfilepath).split('.') 2205 # if len(bn)>0: 2206 # self.rootname = bn[0] 2207 2208 def do(self): 2209 self.update_params() 2210 cml = self.get_cml() 2211 2212 if self.is_clean_osmfile: 2213 for path in self.osmfilepaths.split(','): 2214 path_temp = path+'.clean' 2215 clean_osm(path, path_temp) 2216 #shutil.copy (path_temp, path) 2217 shutil.move(path_temp, path) 2218 2219 # print 'SumonetImporter.do',cml 2220 #import_xml(self, rootname, dirname, is_clean_nodes = True) 2221 self.run_cml(cml) 2222 if self.status == 'success': 2223 if os.path.isfile(self.polyfilepath): 2224 print ' OSM->poly.xml successful, start importing xml files' 2225 self._landuse.import_polyxml(self.rootname, self.workdirpath) 2226 print ' import poly in sumopy done.' 2227 return True 2228 return False 2229 else: 2230 return False 2231 2232 def get_landuse(self): 2233 # used to het landuse in case landuse has been created 2234 return self._landuse 2235 2236 2237if __name__ == '__main__': 2238 ############################################################################### 2239 # print 'sys.path',sys.path 2240 from agilepy.lib_wx.objpanel import objbrowser 2241 from agilepy.lib_base.logger import Logger 2242 #from coremodules.scenario import scenario 2243 from coremodules.network import network 2244 logger = Logger() 2245 NETPATH = os.path.join(SUMOPYDIR, 'coremodules', 'network', 'testnet') 2246 net = network.Network(logger=logger) 2247 rootname = 'facsp2' 2248 net.import_xml(rootname, NETPATH) 2249 # net.read_sumonodes(os.path.join(NETPATH,'facsp2.nod.xml')) 2250 # net.read_sumoedges(os.path.join(NETPATH,'facsp2.edg.xml')) 2251 landuse = Landuse(net=net, logger=logger) 2252 2253 # landuse.facilities.import_poly(os.path.join(NETPATH,'facsp2.poly.xml')) 2254 landuse.import_xml(rootname, NETPATH) 2255 objbrowser(landuse) 2256