Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
eclipse
GitHub Repository: eclipse/sumo
Path: blob/main/tools/contributed/sumopy/coremodules/landuse/landuse.py
169689 views
1
# Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
2
# Copyright (C) 2016-2025 German Aerospace Center (DLR) and others.
3
# SUMOPy module
4
# Copyright (C) 2012-2021 University of Bologna - DICAM
5
# This program and the accompanying materials are made available under the
6
# terms of the Eclipse Public License 2.0 which is available at
7
# https://www.eclipse.org/legal/epl-2.0/
8
# This Source Code may also be made available under the following Secondary
9
# Licenses when the conditions for such availability set forth in the Eclipse
10
# Public License 2.0 are satisfied: GNU General Public License, version 2
11
# or later which is available at
12
# https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
13
# SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
14
15
# @file landuse.py
16
# @author Joerg Schweizer
17
# @date 2012
18
19
import maps
20
from agilepy.lib_base.processes import Process, CmlMixin
21
from collections import OrderedDict
22
from numpy import random
23
import numpy as np
24
from coremodules.misc.shapeformat import ShapefileImporter
25
from coremodules.network.network import SumoIdsConf, MODES
26
from agilepy.lib_base.misc import string_to_float
27
from agilepy.lib_base.geometry import *
28
import agilepy.lib_base.xmlman as xm
29
import agilepy.lib_base.arrayman as am
30
import agilepy.lib_base.classman as cm
31
from coremodules.modules_common import *
32
import os
33
import sys
34
import time
35
import shutil
36
from xml.sax import saxutils, parse, handler
37
if __name__ == '__main__':
38
try:
39
APPDIR = os.path.dirname(os.path.abspath(__file__))
40
except:
41
APPDIR = os.path.dirname(os.path.abspath(sys.argv[0]))
42
SUMOPYDIR = os.path.join(APPDIR, '..', '..')
43
sys.path.append(SUMOPYDIR)
44
try:
45
import pyproj
46
except:
47
from mpl_toolkits.basemap import pyproj
48
49
#from coremodules.network.routing import get_mincostroute_edge2edges
50
51
try:
52
from shapely.geometry import MultiPoint, Polygon
53
IS_SHAPELY = True
54
except:
55
IS_SHAPELY = False
56
57
58
def clean_osm(filepath_in, filepath_out):
59
"""
60
Clean osm file from strange characters that compromize importing.
61
"""
62
#
63
substitutes = {""": "'", "&": "+"}
64
fd_in = open(filepath_in, 'r')
65
fd_out = open(filepath_out, 'w')
66
for line in fd_in.readlines():
67
for oldstr, newstr in substitutes.iteritems():
68
line = line.replace(oldstr, newstr)
69
fd_out.write(line)
70
fd_in.close()
71
fd_out.close()
72
73
74
class LanduseTypes(am.ArrayObjman):
75
def __init__(self, parent, is_add_default=True, **kwargs):
76
77
self._init_objman(ident='landusetypes',
78
parent=parent,
79
name='Landuse types',
80
info='Table with information on landuse types',
81
**kwargs)
82
83
self._init_attributes()
84
if is_add_default:
85
self.add_types_default()
86
87
def _init_attributes(self):
88
# landuse types table
89
self.add_col(am.ArrayConf('typekeys', '',
90
is_index=True,
91
dtype='object', # actually a string of variable length
92
perm='r',
93
name='Type',
94
info='Type of facility. Must be unique, because used as key.',
95
))
96
97
# self.add_col(am.ArrayConf( 'osmid', '',
98
# dtype = 'object',# actually a string of variable length
99
# perm='rw',
100
# name = 'Name',
101
# info = 'Name of facility type used as reference in OSM.',
102
# ))
103
104
self.add_col(am.ArrayConf('colors', np.ones(4, np.float32),
105
dtype=np.float32,
106
metatype='color',
107
perm='rw',
108
name='Colors',
109
info="Color corrispondig to landuse type as RGBA tuple with values from 0.0 to 1.0",
110
xmltag='color',
111
))
112
113
self.add_col(am.ArrayConf('descriptions', '',
114
dtype='object', # actually a string of variable length
115
perm='r',
116
name='Info',
117
info='Information about this landuse.',
118
))
119
120
self.add_col(am.ArrayConf('are_area', False,
121
perm='rw',
122
name='is area',
123
info='True if this is an area of particular use, instead of a single facility. An area can include several facilities. If False, then it is dealt with a single facility.',
124
))
125
126
self.add_col(am.ListArrayConf('osmfilters',
127
perm='r',
128
name='OSM filter',
129
info='List of openstreetmap filters that allow to identify this facility type.',
130
))
131
132
# this will remove all previous and setnw
133
if len(self) > 0:
134
self.clear()
135
self.add_types_default()
136
137
def get_landusetype_facility_from_area(self, id_landuse_area):
138
# get typekey
139
typekey_area = self.typekeys[id_landuse_area]
140
typekey_info = typekey_area.split('_')
141
if len(typekey_info) == 2:
142
if typekey_info[1] == 'area':
143
return self.typekeys.get_id_from_index(typekey_info[0])
144
else:
145
# no area, return unchanged
146
return id_landuse_area
147
else:
148
# no area, return unchanged
149
return id_landuse_area
150
151
def format_ids(self, ids):
152
return ','.join(self.typekeys[ids])
153
154
def get_id_from_formatted(self, idstr):
155
return self.typekeys.get_id_from_index(idstr)
156
157
def get_ids_from_formatted(self, idstrs):
158
return self.typekeys.get_ids_from_indices_save(idstrs.split(','))
159
160
def add_types_default(self):
161
# default types
162
self.add_row(typekeys='leisure',
163
descriptions='Facilities which offer leasure type activities',
164
osmfilters=['historic.church', 'building.church', 'sport', 'sport.*'],
165
colors=(0.2, 0.5, 0.3, 0.7)
166
)
167
# leisure.nature_reserve is actually an area so remove 'leisure.*'
168
169
self.add_row(typekeys='commercial',
170
descriptions='Facility with trade, offices, banks, shopping opportunitties, etc.',
171
osmfilters=['building.hospital', 'building.retail', 'building.shop',
172
'building.restaurant', 'building.cafe', 'restaurant', 'shop.*', 'building.commercial', ],
173
colors=(0.6171875, 0.6171875, 0.875, 0.7),
174
)
175
176
self.add_row(typekeys='industrial',
177
descriptions='industrial production facilities.',
178
osmfilters=['building.industrial'],
179
colors=(0.89453125, 0.65625, 0.63671875, 0.7),
180
)
181
182
self.add_row(typekeys='parking',
183
descriptions='Areas reserved for car parking. This can be either area or building.',
184
osmfilters=['building.parking', ],
185
colors=(0.52734375, 0.875, 0.875, 0.7),
186
)
187
188
self.add_row(typekeys='residential',
189
descriptions='Facilities with residential use',
190
osmfilters=['building.house', 'building.apartments', 'building.*', 'building'],
191
colors=(0.921875, 0.78125, 0.4375, 0.7),
192
)
193
194
self.add_row(typekeys='mixed',
195
descriptions='Facilities with mixed land use, which cannot be clearly assigned to one of the other landuse types.',
196
osmfilters=[],
197
colors=(0.5, 0.9, 0.5, 0.7),
198
)
199
200
self.add_row(typekeys='sink',
201
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.',
202
osmfilters=[],
203
colors=(0.5, 0.0, 0.1, 1.0),
204
)
205
self.add_row(typekeys='education',
206
descriptions='Educational facilities such as schools, universities',
207
# with amenity and landuse, we should look inside and assign all buildings
208
osmfilters=['building.school', 'building.university'],
209
colors=(0.89453125, 0.65625, 0.89453125, 0.7),
210
)
211
212
self.add_row(typekeys='leisure_area',
213
descriptions='Areas on which leasure type activitiesbare offered',
214
osmfilters=['leisure.park', 'park', 'amenity.park',
215
'landuse.park', 'amenity.sport', 'landuse.sport'],
216
are_area=True,
217
colors=(0.2, 0.5, 0.3, 0.7)
218
)
219
220
self.add_row(typekeys='industrial_area',
221
descriptions='Area with industrial production facilities.',
222
osmfilters=['amenity.industrial', 'landuse.industrial', ],
223
are_area=True,
224
colors=(0.89453125, 0.65625, 0.63671875, 0.7),
225
)
226
227
self.add_row(typekeys='commercial_area',
228
descriptions='Areas with trade, offices, banks, shopping opportunitties, or other customer services.',
229
osmfilters=['amenity.hospital', 'amenity.commercial', 'landuse.commercial', ],
230
colors=(0.6171875, 0.6171875, 0.875, 0.7),
231
are_area=True,
232
)
233
234
self.add_row(typekeys='residential_area',
235
descriptions='Areas with residential facilities.',
236
osmfilters=['amenity.residential', 'landuse.residential'],
237
colors=(0.921875, 0.78125, 0.4375, 0.7),
238
are_area=True,
239
)
240
241
self.add_row(typekeys='mixed_area',
242
descriptions='Facilities with mixed land use, which cannot be clearly assigned to one of the other landuse types.',
243
osmfilters=[],
244
colors=(0.5, 0.9, 0.5, 0.7),
245
are_area=True,
246
)
247
248
self.add_row(typekeys='education_area',
249
descriptions='Educational facilities such as schools, universities',
250
osmfilters=['landuse.university', 'landuse.school', 'amenity.school', 'amenity.university'],
251
colors=(0.89453125, 0.65625, 0.89453125, 0.7),
252
are_area=True,
253
)
254
255
self.add_row(typekeys='parking_area',
256
descriptions='Areas reserved for car parking. This can be either area or building.',
257
osmfilters=['amenity.parking'],
258
colors=(0.52734375, 0.875, 0.875, 0.7),
259
are_area=True,
260
)
261
262
263
class Zones(am.ArrayObjman):
264
def __init__(self, parent, edges, **kwargs):
265
self._init_objman(ident='zones', parent=parent,
266
name='Zones',
267
info='Traffic Zones which can be used for zone-to-zone traffic transport demand or to specify zones for traffic evaporation.',
268
is_plugin=True,
269
version=0.3,
270
**kwargs)
271
272
self._init_attributes()
273
self._init_constants()
274
275
def _init_attributes(self):
276
# print 'Zones._init_attributes',hasattr(self,'are_evaporate'),'version',self.get_version()
277
edges = self.parent.get_net().edges
278
self.add_col(SumoIdsConf('Zone', name='Name', perm='rw', info='Unique short name or ID to identify zone.'))
279
280
self.add_col(am.ArrayConf('names_extended', '',
281
dtype=np.object,
282
groupnames=['parameter'],
283
perm='rw',
284
name='Extended name',
285
info='Extended, human readable name, no uniqueness required, not used as key.',
286
))
287
288
self.add_col(am.IdsArrayConf('ids_landusetype', self.parent.landusetypes,
289
id_default=6,
290
#choices = self.parent.landusetypes.typekeys.get_indexmap(),
291
#choiceattrname = 'typekeys',
292
groupnames=['state'],
293
perm='rw',
294
name='Type',
295
info='Zone type. This is actually the landuse type.',
296
))
297
298
self.add_col(am.ArrayConf('coords', np.zeros(3, dtype=np.float32),
299
groupnames=['state'],
300
perm='r',
301
name='Coords',
302
unit='m',
303
info='Zone center coordinates.',
304
is_plugin=True,
305
))
306
307
self.add_col(am.ListArrayConf('shapes',
308
groupnames=['_private'],
309
perm='rw',
310
name='Shape',
311
unit='m',
312
info='List of 3D Shape coordinates delimiting a zone.',
313
is_plugin=True,
314
))
315
316
self.add_col(am.ListArrayConf('areas',
317
groupnames=['state'],
318
perm='rw',
319
name='Area',
320
unit='km^2',
321
info='Area of the zone in square kilometers.',
322
is_plugin=True,
323
))
324
325
# self.add_col(am.IdlistsArrayConf( 'ids_edges_orig', edges,
326
# groupnames = ['state'],
327
# name = 'IDs orig edges',
328
# info = 'List with IDs of network edges that can be used as origins for trips in this zone.',
329
# ))
330
331
# self.add_col(am.IdlistsArrayConf( 'ids_edges_dest', edges,
332
# groupnames = ['state'],
333
# name = 'IDs dest edges',
334
# info = 'List with IDs of network edges that can be used as origins for trips in this zone.',
335
# ))
336
337
if self.get_version() < 0.2:
338
self.delete('priority_max')
339
self.delete('speed_max')
340
self.delete('n_edges_min_length')
341
self.delete('n_edges_max_length')
342
343
# self.add_col(am.ListArrayConf( 'probs_edges_orig',
344
# groupnames = ['_private'],
345
# #perm='rw',
346
# name = 'edge probs origin',
347
# info = 'Probabilities of edges to be at the origin of a trip departing from this zone.',
348
# ))
349
# self.add_col(am.ListArrayConf( 'probs_edges_dest',
350
# groupnames = ['_private'],
351
# #perm='rw',
352
# name = 'edge probs dest',
353
# info = 'Probabilities of edges to be a destination of a trip arriving at this zone.',
354
# ))
355
356
self.add_col(am.IdlistsArrayConf('ids_edges_inside', edges,
357
groupnames=['state'],
358
name='Edge IDs inside',
359
info='List with IDs of network edges that are entirely inside each zone.',
360
))
361
362
if self.get_version() < 0.3:
363
ids = self.get_ids()
364
self.ids_edges_inside[ids] = self.ids_edges_orig[ids]
365
self.delete('ids_edges_dest')
366
self.delete('ids_edges_orig')
367
self.delete('probs_edges_orig')
368
self.delete('probs_edges_dest')
369
370
self.set_version(0.3)
371
372
def _init_constants(self):
373
self._proj = None
374
self._offset = None
375
self._mode_to_edges_inside = {}
376
attrsman = self.get_attrsman()
377
attrsman.do_not_save_attrs(['_proj', '_offset', '_mode_to_edges_inside'])
378
# self.set_version(0.2)
379
380
def make(self, zonename='',
381
coord=np.zeros(3, dtype=np.float32),
382
shape=[],
383
id_landusetype=-1):
384
"""
385
Add a zone
386
"""
387
# print 'Zone.make',coord
388
# print ' shape',type(shape),shape
389
390
self.get_coords_from_shape(shape)
391
id_zone = self.add_row(coords=self.get_coords_from_shape(shape),
392
shapes=shape, areas=find_area(shape)/1000000.,
393
ids_landusetype=id_landusetype,
394
)
395
if zonename == '':
396
self.ids_sumo[id_zone] = str(id_zone)
397
else:
398
self.ids_sumo[id_zone] = zonename
399
400
self.identify_zoneedges(id_zone)
401
# print ' shapes\n',self.shapes.value
402
# print ' zone.shapes[id_zone]\n',self.shapes[id_zone]
403
404
return id_zone
405
406
def format_ids(self, ids):
407
return ','.join(self.ids_sumo[ids])
408
409
def get_id_from_formatted(self, idstr):
410
return self.ids_sumo.get_id_from_index(idstr)
411
412
def get_ids_from_formatted(self, idstrs):
413
return self.ids_sumo.get_ids_from_indices_save(idstrs.split(','))
414
415
def get_coords_from_shape(self, shape):
416
# print 'get_coords_from_shape',np.array(shape),np.mean(np.array(shape),0)
417
return np.mean(np.array(shape), 0)
418
419
def del_element(self, id_zone):
420
# print 'del_element',id_zone
421
self.del_row(id_zone)
422
423
def get_edges(self):
424
return self.ids_edges_inside.get_linktab()
425
426
def update_zones(self):
427
"""Update all dependent attributes of all zones"""
428
ids = self.get_ids()
429
for id_zone, shape in zip(ids, self.shapes[ids]):
430
self.coords[id_zone] = self.get_coords_from_shape(shape)
431
self.areas[id_zone] = find_area(shape)/1000000.0
432
self.identify_zoneedges(id_zone)
433
434
def refresh_zoneedges(self):
435
for _id in self.get_ids():
436
self.identify_zoneedges(_id)
437
# self.make_egdeprobs(_id)
438
self.coords[_id] = self.get_coords_from_shape(self.shapes[_id])
439
440
def refresh_zonearea(self):
441
"""
442
Update the area of each zone
443
"""
444
ids_zone = self.get_ids()
445
for id_zone in ids_zone:
446
self.identify_zonearea(id_zone)
447
448
def update_netoffset(self, deltaoffset):
449
"""
450
Called when network offset has changed.
451
Children may need to adjust theur coordinates.
452
"""
453
# self.zones.update_netoffset(deltaoffset)
454
self.coords.value[:, :2] = self.coords.value[:, :2] + deltaoffset
455
shapes = self.shapes.value
456
for i in xrange(len(shapes)):
457
s = np.array(shapes[i])
458
s[:, :2] = s[:, :2] + deltaoffset
459
shapes[i] = list(s)
460
461
def identify_zonearea(self, id_zone):
462
# print 'identify_zonearea',id_zone
463
shape = self.shapes[id_zone]
464
self.areas[id_zone] = find_area(shape)/1000000.0
465
466
def identify_zoneedges(self, id_zone):
467
print 'identify_zoneedges of id_zone', id_zone
468
469
if len(self.shapes[id_zone]) >= 3:
470
# must have at least 3 vertices to be an area
471
472
inds_within = []
473
ind = 0
474
# print ' self.shapes[id_zone]',self.shapes[id_zone]
475
if not IS_SHAPELY:
476
polygon = np.array(self.shapes[id_zone])[:, :2]
477
# print ' polygon',type(polygon)
478
for polyline in self.get_edges().shapes.get_value():
479
480
# print ' np.array(polyline)[:,:2]',np.array(polyline)[:,:2],type(np.array(polyline)[:,:2])
481
if is_polyline_in_polygon(np.array(polyline)[:, :2], polygon):
482
inds_within.append(ind)
483
ind += 1
484
else:
485
polygon = Polygon(np.array(self.shapes[id_zone])[:, :2])
486
for polyline in self.get_edges().shapes.get_value():
487
if MultiPoint(np.array(polyline)[:, :2]).within(polygon):
488
inds_within.append(ind)
489
ind += 1
490
491
# print ' inds_within',inds_within
492
493
# select and determine weights
494
self.ids_edges_inside[id_zone] = self.get_edges().get_ids(inds_within)
495
print ' found', len(self.ids_edges_inside[id_zone]), 'edges'
496
if len(self.ids_edges_inside[id_zone]) == 0:
497
print 'WARNING in identify_zoneedges: no edges found in zone', id_zone
498
499
def get_zoneedges_by_mode_fast(self, id_zone, id_mode, speed_max=None,
500
modeconst_excl=0.0, modeconst_mix=0.0,
501
weights=None, fstar=None):
502
"""
503
Returns a list of edge IDs which are connected and accessible by mode id_mode.
504
Uses cashed results, if possible.
505
506
Optionally weights and fstar can be provided to improve computational speed.
507
Otherwise weights and fstar are calculated with the optional parameters
508
modeconst_excl and modeconst_mix
509
"""
510
511
# print 'get_zoneedges_by_mode_fast id_zone',id_zone,'id_mode',id_mode
512
if not self._mode_to_edges_inside.has_key(id_mode):
513
self._mode_to_edges_inside[id_mode] = {}
514
515
if not self._mode_to_edges_inside[id_mode].has_key(id_zone):
516
self._mode_to_edges_inside[id_mode][id_zone] = self.get_zoneedges_by_mode(
517
id_zone, id_mode, weights=weights, fstar=fstar)
518
# print ' found edges',len(self._mode_to_edges_inside[id_mode][id_zone])
519
return self._mode_to_edges_inside[id_mode][id_zone]
520
521
def get_zoneedges_by_mode(self, id_zone, id_mode, speed_max=None,
522
modeconst_excl=0.0, modeconst_mix=0.0,
523
weights=None, fstar=None):
524
"""
525
Returns a list of edge IDs which are connected and accessible by mode id_mode.
526
The algorithm tries to maximize the number of reachabe edges.
527
528
Optionally weights and fstar can be provided to improve computational speed.
529
Otherwise weights and fstar are calculated with the optional parameters
530
modeconst_excl and modeconst_mix
531
"""
532
# print 'get_zoneedges_by_mode id_zone',id_zone,'id_mode',id_mode
533
534
return self.get_edges().get_max_connected(
535
ids_edge=self.ids_edges_inside[id_zone],
536
id_mode=id_mode, speed_max=speed_max,
537
modeconst_excl=modeconst_excl,
538
modeconst_mix=modeconst_mix,
539
weights=weights, fstar=fstar,
540
is_bidir=True)
541
542
def get_egdeprobs(self, id_zone, n_edges_min_length, n_edges_max_length, priority_max, speed_max, is_normalize=True, is_dict=False):
543
"""
544
OBSOLETE!!!
545
Returns vectors ids_edge and edge_props
546
where ids_edge are the edge IDs of edges in id_zone and edge_props are
547
the respective probabilities to start/end a trip
548
549
if is_dict is True then a dictionary is returnd with edge IDs as key
550
and arrival/departure probability as value
551
"""
552
# todo: rename ids_edges_orig to simply ids_edges
553
ids_edge_orig = self.ids_edges_orig[id_zone]
554
if ids_edge_orig is None:
555
print 'WARNING: no edges in zone', id_zone, '. Run edge detection first.'
556
if is_dict:
557
return {}
558
else:
559
return [], []
560
561
n_edges_orig = len(ids_edge_orig)
562
563
if n_edges_orig > 0:
564
weights = self.get_edgeweights(ids_edge_orig, n_edges_min_length,
565
n_edges_max_length, priority_max, speed_max)
566
if is_normalize:
567
weights = weights/np.sum(weights)
568
if is_dict:
569
probs = {}
570
for id_edge, prob in zip(ids_edge_orig, weights):
571
probs[id_edge] = prob
572
return probs
573
else:
574
return ids_edge_orig, weights
575
else:
576
if is_dict:
577
return {}
578
else:
579
return [], []
580
581
def make_egdeprobs(self, id_zone, n_edges_min_length, n_edges_max_length, priority_max, speed_max, is_normalize=True):
582
"""
583
OBSOLETE!!! This funzione has moved to origin_to_destination
584
Update probs_edges_orig and probs_edges_dest distribution
585
one for departures and one for arrivals.
586
"""
587
588
# print 'make_egdeprobs',id_zone
589
#zones = self.zones.value
590
#edgeweights_orig = {}
591
#edgeweights_dest = {}
592
593
# for id_zone in zones.get_ids():
594
ids_edge_orig = self.ids_edges_orig[id_zone]
595
ids_edge_dest = self.ids_edges_dest[id_zone]
596
n_edges_orig = len(ids_edge_orig)
597
n_edges_dest = len(ids_edge_dest)
598
# da fare meglio...
599
if n_edges_orig > 0:
600
#self.probs_edges_orig[id_zone] = 1.0/float(n_edges_orig)*np.ones(n_edges_orig,np.float)
601
weights = self.get_edgeweights(ids_edge_orig, n_edges_min_length,
602
n_edges_max_length, priority_max, speed_max)
603
if is_normalize:
604
self.probs_edges_orig[id_zone] = weights/np.sum(weights)
605
else:
606
self.probs_edges_orig[id_zone] = weights
607
else:
608
self.probs_edges_orig[id_zone] = []
609
610
if n_edges_dest > 0:
611
#self.probs_edges_dest[id_zone] = 1.0/float(n_edges_dest)*np.ones(n_edges_dest,np.float)
612
weights = self.get_edgeweights(ids_edge_dest, n_edges_min_length,
613
n_edges_max_length, priority_max, speed_max)
614
if is_normalize:
615
self.probs_edges_dest[id_zone] = weights/np.sum(weights)
616
else:
617
self.probs_edges_dest[id_zone] = weights
618
else:
619
self.probs_edges_dest[id_zone] = []
620
621
def get_edgeweights(self, ids_edge, n_edges_min_length, n_edges_max_length, priority_max, speed_max):
622
# OBSOLETE!!! This funzione has moved to origin_to_destination
623
# print 'get_edgeweights ids_edge',ids_edge
624
edges = self.get_edges()
625
n_edges = len(ids_edge)
626
if (n_edges > n_edges_min_length) & (n_edges < n_edges_max_length):
627
return edges.lengths[ids_edge]*((edges.priorities[ids_edge] < priority_max) & (edges.speeds_max[ids_edge] < speed_max))
628
else:
629
return np.ones(n_edges, dtype=np.float32)*((edges.priorities[ids_edge] < priority_max) & (edges.speeds_max[ids_edge] < speed_max))
630
631
def write_kml(self, fd=None, indent=0):
632
633
ids_zone = self.get_ids()
634
for id_zone in ids_zone:
635
fd.write(xm.begin('Placemark', indent + 2))
636
fd.write((indent + 4)*' '+'<name>%s</name>\n' % self.names_extended[id_zone])
637
fd.write(xm.begin('LineString', indent + 4))
638
fd.write(xm.begin('coordinates', indent + 6))
639
640
for point in self.shapes[id_zone]:
641
642
projection = self.project(point[0], point[1])
643
fd.write((indent+8)*' '+'%f,%f\n' % (projection[0], projection[1]))
644
645
fd.write(xm.end('coordinates', indent + 6))
646
fd.write(xm.end('LineString', indent + 4))
647
fd.write(xm.end('Placemark', indent + 2))
648
649
def write_xml(self, fd=None, indent=0):
650
print 'Zones.write_xml'
651
net = self.parent.parent.net
652
ids_edge_sumo = net.edges.ids_sumo
653
ids_zone = self.get_ids()
654
fd.write(xm.begin('tazs', indent))
655
for id_zone_sumo, shape, id_edges_orig, id_edges_dest, probs_edge_orig, probs_edge_dest in zip(
656
self.ids_sumo[ids_zone], self.shapes[ids_zone],
657
self.ids_edges_orig[ids_zone], self.ids_edges_dest[ids_zone],
658
self.probs_edges_orig[ids_zone], self.probs_edges_dest[ids_zone]):
659
660
if id_edges_orig is not None:
661
if len(id_edges_orig) > 0:
662
# print ' id_zone_sumo',id_zone_sumo,'id_edges_orig',id_edges_orig,'probs_edge_orig',probs_edge_orig
663
# <taz id="<TAZ_ID>">
664
# <tazSource id="<EDGE_ID>" weight="<PROBABILITY_TO_USE>"/>
665
# ... further source edges ...
666
#
667
# <tazSink id="<EDGE_ID>" weight="<PROBABILITY_TO_USE>"/>
668
# ... further destination edges ...
669
# </taz>
670
671
fd.write(xm.start('taz', indent + 2))
672
fd.write(xm.num('id', id_zone_sumo))
673
fd.write(xm.mat('shape', shape))
674
fd.write(xm.stop())
675
# for id_edge_sumo, prob_edge in zip(ids_edge_sumo[id_edges_orig], probs_edge_orig):
676
# fd.write(xm.start('tazSource',indent + 4))
677
# fd.write(xm.num('id',id_edge_sumo))
678
# fd.write(xm.num('weight',prob_edge))
679
# fd.write(xm.stopit())
680
#
681
# for id_edge_sumo, prob_edge in zip(ids_edge_sumo[id_edges_dest], probs_edge_dest):
682
# fd.write(xm.start('tazSink',indent + 4))
683
# fd.write(xm.num('id',id_edge_sumo))
684
# fd.write(xm.num('weight',prob_edge))
685
# fd.write(xm.stopit())
686
687
fd.write(xm.end('taz', indent + 2))
688
689
fd.write(xm.end('tazs', indent))
690
691
def project(self, x, y):
692
if self._proj is None:
693
self._proj, self._offset = self.get_proj_and_offset()
694
lons, lats = self._proj(x-self._offset[0], y-self._offset[1], inverse=True)
695
return np.transpose(np.concatenate(([lons], [lats]), axis=0))
696
697
def get_proj_and_offset(self):
698
if self._proj is None:
699
net = self.parent.parent.net
700
proj_params = str(net.get_projparams())
701
# try:
702
self._proj = pyproj.Proj(proj_params)
703
# except:
704
# proj_params ="+proj=utm +zone=32 +ellps=WGS84 +datum=WGS84 +units=m +no_defs"
705
# self._proj = pyproj.Proj(self.proj_params)
706
707
self._offset = net.get_offset()
708
709
return self._proj, self._offset
710
711
def get_zonefilepath(self):
712
return self.parent.parent.get_rootfilepath()+'.taz'
713
714
def export_kml(self, filepath=None, encoding='UTF-8'):
715
"""
716
Export zones to Google kml file formate.
717
"""
718
print 'export_sumoxml', filepath, len(self)
719
if len(self) == 0:
720
return None
721
722
if filepath is None:
723
filepath = self.get_zonefilepath()+'.kml'
724
725
try:
726
fd = open(filepath, 'w')
727
except:
728
print 'WARNING in write_obj_to_xml: could not open', filepath
729
return False
730
#xmltag, xmltag_item, attrname_id = self.xmltag
731
fd.write('<?xml version="1.0" encoding="%s"?>\n' % encoding)
732
fd.write('<kml xmlns="http://www.opengis.net/kml/2.2">\n')
733
indent = 0
734
#fd.write(xm.begin('routes xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://sumo.sf.net/xsd/routes_file.xsd"',indent))
735
736
fd.write(xm.begin('Document', indent=0))
737
738
self.write_kml(fd, indent=0)
739
740
fd.write(xm.end('Document', indent=0))
741
fd.write(xm.end('kml', indent=0))
742
fd.close()
743
return filepath
744
745
def export_sumoxml(self, filepath=None, encoding='UTF-8'):
746
"""
747
Export zones to SUMO xml file formate.
748
"""
749
print 'export_sumoxml', filepath, len(self)
750
if len(self) == 0:
751
return None
752
753
if filepath is None:
754
filepath = self.get_zonefilepath()+'.xml'
755
756
try:
757
fd = open(filepath, 'w')
758
except:
759
print 'WARNING in export_sumoxml: could not open', filepath
760
return False
761
#xmltag, xmltag_item, attrname_id = self.xmltag
762
#fd.write('<?xml version="1.0" encoding="%s"?>\n'%encoding)
763
#fd.write('<kml xmlns="http://www.opengis.net/kml/2.2">\n')
764
indent = 0
765
#fd.write(xm.begin('routes xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://sumo.sf.net/xsd/routes_file.xsd"',indent))
766
767
self.write_xml(fd, indent=0)
768
769
fd.close()
770
return filepath
771
772
773
class ZonesFromShapeImporter(ShapefileImporter):
774
def __init__(self, ident='zonesfromshapeimporter', zones=None,
775
name='Zones from Shapefile importer',
776
shapeattr_ids_zone='ID',
777
shapeattr_names_zone='',
778
shapeattr_names_landuse='',
779
filepath='', logger=None, **kwargs):
780
781
net = zones.parent.parent.net
782
ShapefileImporter.__init__(self, ident, parent=zones,
783
name=name,
784
filepath=filepath,
785
coordsattr='shapes',
786
attrnames_to_shapeattrs={'ids_sumo': shapeattr_ids_zone,
787
'names_extended': shapeattr_names_zone,
788
'ids_landusetype': shapeattr_names_landuse},
789
projparams_target=net.get_projparams(),
790
offset=net.get_offset(),
791
boundaries=net.get_boundaries(is_netboundaries=True),
792
logger=logger,
793
**kwargs)
794
795
def do(self):
796
ShapefileImporter.do(self)
797
798
# update zones
799
self.parent.update_zones()
800
801
802
class FacilityTypeMixin(cm.BaseObjman):
803
def __init__(self, ident, parent,
804
name='Facility Type Mixin',
805
info='Provides methods to handle specific facility functions.',
806
**kwargs):
807
"""
808
To be overridden.
809
"""
810
# attention parent is the Strategies table
811
self._init_objman(ident, parent, **kwargs)
812
attrsman = self.set_attrsman(cm.Attrsman(self))
813
self._init_attributes()
814
self._init_constants()
815
816
def _init_attributes(self):
817
self._init_attributes_common()
818
819
def _init_attributes_common(self):
820
# print 'StrategyMixin._init_attributes'
821
attrsman = self.get_attrsman()
822
landusetypes = self.get_landuse().landusetypes
823
self.ids_landusetype = attrsman.add(cm.AttrConf('ids_landusetype',
824
[landusetypes.get_id_from_formatted('residential'), ],
825
groupnames=['parameters'],
826
perm='rw',
827
#choices = landusetypes.typekeys.get_indexmap(),
828
name='landuse',
829
info='Default landuse type of this facility.',
830
))
831
832
self.osmkey = attrsman.add(cm.AttrConf('osmkey', 'building.yes',
833
groupnames=['parameters'],
834
perm='rw',
835
name='OSM key',
836
info='Default Open Street Map key for this facility.',
837
))
838
839
self.length_min = attrsman.add(cm.AttrConf('length_min', 15.0,
840
groupnames=['parameters'],
841
perm='rw',
842
name='Min. length',
843
unit='m',
844
info='Minimum length of entire property.',
845
))
846
847
self.length_max = attrsman.add(cm.AttrConf('length_max', 100.0,
848
groupnames=['parameters'],
849
perm='rw',
850
name='Max. length',
851
unit='m',
852
info='Maximum length of entire property.',
853
))
854
self.width_min = attrsman.add(cm.AttrConf('width_min', 20.0,
855
groupnames=['parameters'],
856
perm='rw',
857
name='Min. width',
858
unit='m',
859
info='Minimum width of entire property.',
860
))
861
862
self.width_max = attrsman.add(cm.AttrConf('width_max', 80.0,
863
groupnames=['parameters'],
864
perm='rw',
865
name='Max. width',
866
unit='m',
867
info='Maximum width of entire property.',
868
))
869
870
self.height_min = attrsman.add(cm.AttrConf('height_min', 15.0,
871
groupnames=['parameters'],
872
perm='rw',
873
name='Min. height',
874
unit='m',
875
info='Minimum height of facility.',
876
))
877
878
self.height_max = attrsman.add(cm.AttrConf('height_max', 35.0,
879
groupnames=['parameters'],
880
perm='rw',
881
name='Max. height',
882
unit='m',
883
info='Maximum height of facility.',
884
))
885
886
self.dist_road_min = attrsman.add(cm.AttrConf('dist_road_min', 1.0,
887
groupnames=['parameters'],
888
perm='rw',
889
name='Min. dist road',
890
unit='m',
891
info='Minimum distance from road.',
892
))
893
894
self.dist_road_max = attrsman.add(cm.AttrConf('dist_road_max', 5.0,
895
groupnames=['parameters'],
896
perm='rw',
897
name='Max. dist road',
898
info='Maximum distance from road.',
899
))
900
901
self.dist_prop_min = attrsman.add(cm.AttrConf('dist_prop_min', 1.0,
902
groupnames=['parameters'],
903
perm='rw',
904
name='Min. prop dist',
905
unit='m',
906
info='Minimum distance to other properties.',
907
))
908
909
self.dist_prop_max = attrsman.add(cm.AttrConf('dist_prop_max', 4.0,
910
groupnames=['parameters'],
911
perm='rw',
912
name='Max. prop dist',
913
info='Maximum distance to other properties.',
914
))
915
916
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]],
917
groupnames=['parameters'],
918
perm='r',
919
name='Defaut shape',
920
info='Basic facility shape.',
921
))
922
923
self.unitvolume_default = attrsman.add(cm.AttrConf('unitvolume_default',
924
default=100.0,
925
dtype=np.float32,
926
perm='r',
927
name='Unit volume',
928
info='Default value of the volume necessary to store one person or container. Volume used to calculate capacity.',
929
))
930
931
def _init_constants(self):
932
933
# self.get_attrsman().do_not_save_attrs([
934
# '_id_mode_bike','_id_mode_auto','_id_mode_moto',
935
# ])
936
pass
937
938
def get_id_type(self):
939
# print 'get_id_type from ',self.parent.get_ident_abs()
940
# print ' names',self.parent.names.get_value()
941
return self.parent.names.get_id_from_index(self.get_ident())
942
943
def get_facilities(self):
944
return self.parent.parent
945
946
def get_landuse(self):
947
return self.parent.parent.parent
948
949
def get_scenario(self):
950
return self.parent.parent.get_scenario()
951
952
def get_shape0(self, length_fac, width_fac, height_max, capacity):
953
"""
954
Returns facility shape in origin coordinate system
955
and height as a function of the capacity and available space
956
"""
957
# One could do some random operations on the default shape
958
# here just stretch default shape to fit area
959
shape = np.array(self.shape_default, dtype=np.float32) * [length_fac, width_fac, 0.0]
960
# y-axes must be flipped so that hoses grow
961
# to the right side of the road
962
shape[:, 1] *= -1
963
# Calculates height of the building in function of the
964
# required capacity and available space.
965
966
# here just use a random number
967
# to be overridden
968
height = min(random.uniform(self.height_min, self.height_max), height_max)
969
970
return shape, height
971
972
def generate(self, offset=[0.0, 0.0, 0.0],
973
length=10.0,
974
width=10.0,
975
angle=0.0,
976
pos_edge=0.0,
977
capacity=None,
978
id_landusetype=None,
979
height_max=30.0,
980
id_edge=None,
981
width_edge=3.0,
982
):
983
n_shape = len(self.shape_default)
984
shape_fac = np.zeros((n_shape, 3), dtype=np.float32)
985
986
# determine effecive land area
987
dist_edge = random.uniform(self.dist_road_min, self.dist_road_max) + width_edge
988
width_fac = width-dist_edge
989
990
dist_prop = random.uniform(self.dist_prop_min, self.dist_prop_max)
991
length_fac = length-2*self.dist_prop_max
992
993
# do offset
994
dxn = np.cos(angle-np.pi/2)
995
dyn = np.sin(angle-np.pi/2)
996
997
offset_fac = offset\
998
+ np.array([dxn, dyn, 0.0], dtype=np.float32)*dist_edge\
999
+ np.array([dyn, dxn, 0.0], dtype=np.float32)*dist_prop
1000
1001
shape, height_fac = self.get_shape0(length_fac, width_fac, height_max, capacity)
1002
1003
# transform in to the right place
1004
shape_fac[:, 0] = shape[:, 0]*np.cos(angle) - shape[:, 1]*np.sin(angle)
1005
shape_fac[:, 1] = shape[:, 0]*np.sin(angle) + shape[:, 1]*np.cos(angle)
1006
shape_fac += offset_fac
1007
1008
if id_landusetype is None:
1009
id_landusetype = self.ids_landusetype[0]
1010
1011
id_fac = self.get_facilities().make(id_landusetype=id_landusetype,
1012
id_zone=None,
1013
id_facilitytype=self.get_id_type(),
1014
osmkey=self.osmkey,
1015
area=None,
1016
height=height_fac,
1017
centroid=None,
1018
shape=list(shape_fac),
1019
id_roadedge_closest=id_edge,
1020
position_roadedge_closest=pos_edge + 0.5 * length,
1021
)
1022
1023
1024
class FacilityTypeHouse(FacilityTypeMixin):
1025
def __init__(self, ident, parent,
1026
name='Single House',
1027
info='Parameters and methods for residential house.',
1028
**kwargs):
1029
1030
self._init_objman(ident, parent, name=name, info=info, **kwargs)
1031
attrsman = self.set_attrsman(cm.Attrsman(self))
1032
1033
self._init_attributes()
1034
self._init_constants()
1035
# specific init
1036
1037
1038
class FacilityTypes(am.ArrayObjman):
1039
def __init__(self, ident, facilities, is_add_default=True, **kwargs):
1040
self._init_objman(ident=ident,
1041
parent=facilities,
1042
name='Facility types',
1043
info='Table holding facility type specific parameters and an object with methods ',
1044
**kwargs)
1045
1046
self._init_attributes()
1047
if is_add_default:
1048
self.add_default()
1049
1050
def _init_attributes(self):
1051
# landuse types table
1052
self.add_col(am.ArrayConf('names',
1053
default='',
1054
dtype='object',
1055
perm='r',
1056
is_index=True,
1057
name='Short name',
1058
info='Strategy name. Must be unique, used as index.',
1059
))
1060
1061
self.add_col(am.ArrayConf('unitvolumes',
1062
default=100.0,
1063
dtype=np.float32,
1064
perm='rw',
1065
name='Unit volume',
1066
info='The volume necessary to store one person or container. Volume used to calculate capacity.',
1067
))
1068
1069
self.add_col(cm.ObjsConf('typeobjects',
1070
#groupnames = ['state'],
1071
name='Type objects',
1072
info='Facility type object.',
1073
))
1074
1075
self.add_default()
1076
1077
def format_ids(self, ids):
1078
return ','.join(self.names[ids])
1079
1080
def get_id_from_formatted(self, idstr):
1081
return self.names.get_id_from_index(idstr)
1082
1083
def get_ids_from_formatted(self, idstrs):
1084
return self.names.get_ids_from_indices_save(idstrs.split(','))
1085
1086
def add_default(self):
1087
self.clear()
1088
self.add_type('house', FacilityTypeHouse)
1089
1090
def get_typeobj(self, id_type):
1091
return self.typeobjects[id_type]
1092
1093
def add_type(self, ident, TypeClass, **kwargs):
1094
# print 'add_strategy', ident
1095
if not self.names.has_index(ident):
1096
factypeobj = TypeClass(ident, self)
1097
id_type = self.add_row(names=ident,
1098
typeobjects=factypeobj,
1099
unitvolumes=factypeobj.unitvolume_default
1100
)
1101
return id_type
1102
else:
1103
return self.get_id_from_formatted(id_type)
1104
1105
1106
class Facilities(am.ArrayObjman):
1107
def __init__(self, landuse, landusetypes, zones, net=None, **kwargs):
1108
# print 'Facilities.__init__',hasattr(self,'lanes')
1109
self._init_objman(ident='facilities',
1110
parent=landuse,
1111
name='Facilities',
1112
info='Information on buildings, their type of usage and access to the transport network.',
1113
xmltag=('polys', 'poly', 'ids_sumo'),
1114
is_plugin=True,
1115
**kwargs)
1116
1117
self._init_attributes()
1118
self._init_constants()
1119
1120
if net is not None:
1121
self.add_col(am.IdsArrayConf('ids_roadedge_closest', net.edges,
1122
groupnames=['landuse'],
1123
name='Road edge ID',
1124
info='ID of road edge which is closest to this facility.',
1125
))
1126
1127
self.add_col(am.ArrayConf('positions_roadedge_closest', 0.0,
1128
dtype=np.float32,
1129
groupnames=['landuse'],
1130
perm='r',
1131
name='Road edge pos',
1132
unit='m',
1133
info='Position on road edge which is closest to this facility',
1134
))
1135
1136
# self.ids_stop_closest = self.facilities.add(cm.ArrayConf( 'ids_stop_closest', None,
1137
# dtype = 'object',
1138
# name = 'ID stops',
1139
# perm='rw',
1140
# info = 'List of IDs of closest public transport stops.',
1141
# ))
1142
1143
def _init_constants(self):
1144
self._proj = None
1145
self._offset = None
1146
1147
attrsman = self.get_attrsman()
1148
attrsman.do_not_save_attrs(['_proj', '_offset'])
1149
1150
def _init_attributes(self):
1151
landusetypes = self.parent.landusetypes
1152
zones = self.parent.zones
1153
self.add(cm.ObjConf(FacilityTypes('facilitytypes', self,)))
1154
1155
self.add_col(SumoIdsConf('Facility', info='SUMO facility ID'))
1156
1157
self.add_col(am.ArrayConf('names', '',
1158
dtype='object', # actually a string of variable length
1159
perm='rw',
1160
name='Name',
1161
info='Name of facility.',
1162
is_plugin=True,
1163
))
1164
1165
self.add_col(am.IdsArrayConf('ids_landusetype', landusetypes,
1166
groupnames=['landuse'],
1167
perm='rw',
1168
name='ID landuse',
1169
info='ID of landuse.',
1170
is_plugin=True,
1171
))
1172
1173
self.add_col(am.IdsArrayConf('ids_zone', zones,
1174
groupnames=['landuse'],
1175
perm='r',
1176
name='ID zone',
1177
info='ID of traffic zone, where this facility is located.',
1178
))
1179
facilitytypes = self.get_facilitytypes()
1180
if len(facilitytypes) > 0:
1181
id_default = facilitytypes.get_ids()[0]
1182
else:
1183
id_default = -1
1184
self.add_col(am.IdsArrayConf('ids_facilitytype', facilitytypes,
1185
id_default=id_default,
1186
groupnames=['landuse'],
1187
perm='rw',
1188
name='ID fac. type',
1189
info='ID of facility type (house, scycraper, factory, parking,...).',
1190
is_plugin=True,
1191
))
1192
1193
self.add_col(am.ArrayConf('osmkeys', 'building.yes',
1194
dtype='object', # actually a string of variable length
1195
perm='rw',
1196
name='OSM key',
1197
info='OSM key of facility.',
1198
xmltag='type',
1199
is_plugin=True,
1200
))
1201
1202
self.add_col(am.ArrayConf('capacities', 0,
1203
dtype=np.int32,
1204
groupnames=['landuse'],
1205
perm='r',
1206
name='Capacity',
1207
info='Person capacity of this facility. For example maximum number of adulds living in a building or number of people working in a factory.',
1208
))
1209
1210
self.add_col(am.ArrayConf('areas', 0.0,
1211
dtype=np.float32,
1212
groupnames=['landuse'],
1213
perm='r',
1214
name='Area',
1215
unit='m^2',
1216
info='Area of this facility.',
1217
))
1218
1219
self.add_col(am.ArrayConf('heights', 10.0,
1220
dtype=np.float32,
1221
groupnames=['landuse'],
1222
perm='r',
1223
name='Height',
1224
unit='m',
1225
info='Height above ground of this facility.',
1226
))
1227
1228
self.add_col(am.ArrayConf('centroids', np.zeros(3, dtype=np.float32),
1229
dtype=np.float32,
1230
groupnames=['state', '_private'],
1231
perm='r',
1232
name='Center',
1233
unit='m',
1234
info='Center coordinates of this facility.',
1235
))
1236
1237
self.add_col(am.ListArrayConf('shapes',
1238
groupnames=['_private'],
1239
perm='rw',
1240
name='Shape',
1241
unit='m',
1242
info='List of 3D Shape coordinates of facility.',
1243
xmltag='shape',
1244
is_plugin=True,
1245
))
1246
1247
# self.add_col(TabIdsArrayConf( 'ftypes',
1248
# name = 'Types',
1249
# info = 'Draw obj and ids',
1250
# ))
1251
1252
if self.plugin is None:
1253
self.init_plugin(True)
1254
self.shapes.init_plugin(True)
1255
self.osmkeys.init_plugin(True)
1256
self.ids_landusetype.init_plugin(True)
1257
# configure only if net is initialized
1258
1259
def make(self, id_sumo=None,
1260
id_landusetype=None,
1261
id_zone=None,
1262
id_facilitytype=None,
1263
osmkey=None,
1264
area=None,
1265
capacity=None,
1266
height=None,
1267
centroid=None,
1268
shape=[],
1269
id_roadedge_closest=None,
1270
position_roadedge_closest=None,
1271
):
1272
"""
1273
Adds a facilities
1274
"""
1275
id_fac = self.suggest_id()
1276
if id_sumo is None:
1277
id_sumo = str(id_fac)
1278
1279
# stuff with landusetype must be done later
1280
id_fac = self.add_row(_id=id_fac,
1281
ids_sumo=id_sumo,
1282
ids_landusetype=id_landusetype,
1283
ids_zone=id_zone,
1284
ids_facilitytype=id_facilitytype,
1285
osmkeys=osmkey,
1286
areas=area,
1287
capacities=capacity,
1288
heights=height,
1289
centroids=centroid,
1290
shapes=shape,
1291
ids_roadedge_closest=id_roadedge_closest,
1292
positions_roadedge_closest=position_roadedge_closest,
1293
)
1294
# do area calcs and other
1295
if area is None:
1296
self.update_area(id_fac)
1297
1298
if capacity is None:
1299
self.update_capacity(id_fac)
1300
1301
if centroid is None:
1302
self.update_centroid(id_fac)
1303
return id_fac
1304
1305
def generate(self, facilitytype, **kwargs):
1306
"""
1307
Generates a facility. The generation of the facility will be
1308
performed by the faciliy type instance.
1309
"""
1310
return facilitytype.generate(**kwargs)
1311
1312
def format_ids(self, ids):
1313
return ','.join(self.ids_sumo[ids])
1314
1315
def get_id_from_formatted(self, idstr):
1316
return self.ids_sumo.get_id_from_index(idstr)
1317
1318
def get_ids_from_formatted(self, idstrs):
1319
return self.ids_sumo.get_ids_from_indices_save(idstrs.split(','))
1320
1321
def del_element(self, id_fac):
1322
# print 'del_element',id_zone
1323
self.del_row(id_fac)
1324
1325
def write_xml(self, fd, indent=0, is_print_begin_end=True, delta=np.zeros(3, dtype=np.float32)):
1326
xmltag, xmltag_item, attrname_id = self.xmltag
1327
layer_default = -1
1328
fill_default = 1
1329
ids_landusetype = self.ids_landusetype
1330
landusecolors = self.get_landusetypes().colors
1331
1332
if is_print_begin_end:
1333
fd.write(xm.begin(xmltag, indent))
1334
1335
attrsconfigs_write = [self.ids_sumo, self.osmkeys]
1336
xmltag_shape = self.shapes.xmltag
1337
sep_shape = self.shapes.xmlsep
1338
for _id in self.get_ids():
1339
fd.write(xm.start(xmltag_item, indent+2))
1340
for attrsconfig in attrsconfigs_write:
1341
attrsconfig.write_xml(fd, _id)
1342
1343
fd.write(xm.mat(xmltag_shape, self.shapes[_id] - delta))
1344
1345
landusecolors.write_xml(fd, ids_landusetype[_id])
1346
fd.write(xm.num('layer', layer_default))
1347
fd.write(xm.num('fill', fill_default))
1348
1349
fd.write(xm.stopit())
1350
1351
if is_print_begin_end:
1352
fd.write(xm.end(xmltag, indent))
1353
1354
def get_landusetypes(self):
1355
return self.ids_landusetype.get_linktab()
1356
1357
def get_facilitytypes(self):
1358
return self.facilitytypes.get_value()
1359
1360
def get_net(self):
1361
# print 'get_net',self.ids_edge_closest_road.get_linktab(),self.ids_edge_closest_road.get_linktab().parent
1362
return self.ids_roadedge_closest.get_linktab().parent
1363
1364
def get_scenario(self):
1365
return self.ids_roadedge_closest.get_linktab().parent.parent
1366
1367
def update_netoffset(self, deltaoffset):
1368
"""
1369
Called when network offset has changed.
1370
Children may need to adjust theur coordinates.
1371
"""
1372
# self.zones.update_netoffset(deltaoffset)
1373
self.centroids.value[:, :2] = self.centroids.value[:, :2] + deltaoffset
1374
shapes = self.shapes.value
1375
for i in xrange(len(shapes)):
1376
s = np.array(shapes[i])
1377
s[:, :2] = s[:, :2] + deltaoffset
1378
shapes[i] = list(s)
1379
1380
def get_edges(self):
1381
return self.ids_roadedge_closest.get_linktab()
1382
1383
def identify_taz(self):
1384
"""
1385
Identifies id of traffic assignment zone for each facility.
1386
Note that not all facilities are within such a zone.
1387
"""
1388
zones = self.ids_zone.get_linktab()
1389
# self.get_demand().get_districts()
1390
for id_fac in self.get_ids():
1391
for id_zone in zones.get_ids():
1392
if is_polyline_in_polygon(self.shapes[id_fac], zones.shapes[id_zone]):
1393
self.ids_zone[id_fac] = id_zone
1394
break
1395
1396
def get_departure_probabilities(self):
1397
"""
1398
Returns a dictionary, where probabilities[id_zone]
1399
is a vector of departure probabilities for each facility
1400
of zone id_zone.
1401
"""
1402
zones = self.ids_zone.get_linktab()
1403
# print 'get_departure_probabilities in n_zones',len(zones)
1404
probabilities = {}
1405
1406
inds_fac = self.get_inds()
1407
for id_zone in zones.get_ids():
1408
# probabilities[id_zone]={}
1409
# for id_landusetype in set(self.ids_landusetype.value):
1410
# print ' id_zone',id_zone
1411
# print ' ids_landusetype',self.ids_landusetype.value[inds_fac]
1412
# print ' ids_zone',self.ids_zone.value[inds_fac]
1413
# print ''
1414
util = self.capacities.value[inds_fac].astype(np.float32)*(self.ids_zone.value[inds_fac] == id_zone)
1415
util_tot = np.sum(util)
1416
# print '\n\n [id_taz][ftype]',id_taz,ftype,util_tot,np.sum(util/np.sum(util))
1417
# print ' self.type==ftype',self.type==ftype
1418
# print ' self.id_taz==id_taz',self.id_taz==id_taz
1419
# print ' util',util
1420
if util_tot > 0.0:
1421
probabilities[id_zone] = util/util_tot
1422
else:
1423
probabilities[id_zone] = util # all zero prob
1424
1425
return probabilities, self.get_ids(inds_fac)
1426
1427
def get_departure_probabilities_landuse2(self, ids_landusetype):
1428
"""
1429
Returns a dictionary, where probabilities[id_zone]
1430
is a vector of departure probabilities for each facility
1431
of zone id_zone.
1432
Probabilities are proportional to the capacity attribute of the facility.
1433
1434
Only facilities with one of the landuse given in ids_landusetype
1435
have non-zero probabilities.
1436
1437
The ids_fac is an array that contains the facility ids in correspondence
1438
to the probability vector.
1439
"""
1440
print 'get_departure_probabilities_landuse2 ids_landusetype', ids_landusetype
1441
probabilities = {}
1442
zones = self.ids_zone.get_linktab()
1443
inds_fac = self.get_inds()
1444
n_frac = len(inds_fac)
1445
for id_zone in zones.get_ids():
1446
# probabilities[id_zone]={}
1447
utils = np.zeros(n_frac, dtype=np.float32)
1448
util_tot = 0.0
1449
for id_landusetype in ids_landusetype:
1450
# print ' id_zone,id_landusetype',id_zone,id_landusetype
1451
# print ' ids_landusetype',self.ids_landusetype.value[inds_fac]
1452
# print ' ids_zone',self.ids_zone.value[inds_fac]
1453
# print ''
1454
utils_new = self.capacities.value[inds_fac].astype(
1455
np.float32)*np.logical_and((self.ids_landusetype.value[inds_fac] == id_landusetype), (self.ids_zone.value[inds_fac] == id_zone))
1456
utils += utils_new
1457
util_tot += np.sum(utils_new)
1458
# print '\n\n [id_taz][ftype]',id_taz,ftype,util_tot,np.sum(util/np.sum(util))
1459
# print ' self.type==ftype',self.type==ftype
1460
# print ' self.id_taz==id_taz',self.id_taz==id_taz
1461
# print ' util',np.sum(utils)
1462
1463
if util_tot > 0.0:
1464
probabilities[id_zone] = utils/util_tot
1465
else:
1466
probabilities[id_zone] = utils # all zero prob
1467
1468
if 0: # debug
1469
print ' sum(probs)', np.sum(probabilities[id_zone])
1470
if np.sum(probabilities[id_zone]) > 0:
1471
ids_fac = self.get_ids(inds_fac)
1472
for id_fac, id_landusetype, id_thiszone, prob in zip(ids_fac, self.ids_landusetype[ids_fac], self.ids_zone[ids_fac], probabilities[id_zone]):
1473
if (id_thiszone == id_zone): # & (id_landusetype in ids_landusetype):
1474
if prob > 0:
1475
print ' id_fac', id_fac, 'id_landusetype', id_landusetype, 'prob', prob
1476
1477
return probabilities, self.get_ids(inds_fac)
1478
1479
def get_departure_probabilities_landuse(self):
1480
"""
1481
Returns the dictionnary of dictionaries with departure (or arrival)
1482
probabilities where probabilities[id_zone][id_landusetype]
1483
is a probability distribution vector giving for each facility the
1484
probability to depart/arrive in zone id_zone with facility type ftype.
1485
1486
The ids_fac is an array that contains the facility ids in correspondence
1487
to the probability vector.
1488
"""
1489
print 'get_departure_probabilities_landuse'
1490
probabilities = {}
1491
zones = self.ids_zone.get_linktab()
1492
inds_fac = self.get_inds()
1493
for id_zone in zones.get_ids():
1494
probabilities[id_zone] = {}
1495
for id_landusetype in set(self.ids_landusetype.value):
1496
print ' id_zone,id_landusetype', id_zone, id_landusetype
1497
# print ' ids_landusetype',self.ids_landusetype.value[inds_fac]
1498
# print ' ids_zone',self.ids_zone.value[inds_fac]
1499
# print ''
1500
util = self.capacities.value[inds_fac].astype(
1501
np.float32)*((self.ids_landusetype.value[inds_fac] == id_landusetype) & (self.ids_zone.value[inds_fac] == id_zone))
1502
util_tot = np.sum(util)
1503
# print '\n\n [id_taz][ftype]',id_taz,ftype,util_tot,np.sum(util/np.sum(util))
1504
# print ' self.type==ftype',self.type==ftype
1505
# print ' self.id_taz==id_taz',self.id_taz==id_taz
1506
# print ' util',util
1507
if util_tot > 0.0:
1508
probabilities[id_zone][id_landusetype] = util/util_tot
1509
else:
1510
probabilities[id_zone][id_landusetype] = util # all zero prob
1511
1512
return probabilities, self.get_ids(inds_fac)
1513
1514
def update(self, ids=None):
1515
# print 'update',ids
1516
if ids is None:
1517
ids = self.get_ids()
1518
1519
for _id in ids:
1520
# print ' self.centroids[_id]',self.centroids[_id]
1521
# print ' self.shapes[_id]',self.shapes[_id],np.mean(self.shapes[_id],0)
1522
self.update_centroid(_id)
1523
#self.areas[_id] = find_area(np.array(self.shapes[_id],float)[:,:2])
1524
self.update_area(_id)
1525
#self.areas[_id] = get_polygonarea_fast(np.array(self.shapes[_id],float)[:,0], np.array(self.shapes[_id],float)[:,1])
1526
1527
self.identify_landuse_from_area(ids)
1528
self.update_capacities(ids)
1529
# self.identify_closest_edge(ids)
1530
1531
def get_ids_area(self, ids=None):
1532
if ids is None:
1533
ids = self.get_ids()
1534
return ids[self.get_landusetypes().are_area[self.ids_landusetype[ids]]]
1535
1536
def get_ids_building(self, ids=None):
1537
"""Returns all building type of facilities"""
1538
# print 'get_ids_building'
1539
if ids is None:
1540
ids = self.get_ids()
1541
# debug
1542
#landusetypes = self.get_landusetypes()
1543
# for id_fac in ids[self.get_landusetypes().are_area[self.ids_landusetype[ids]] == False]:
1544
# id_landusetype = self.ids_landusetype[id_fac]
1545
# print ' id_fac',id_fac,id_landusetype,'is_area',landusetypes.are_area[id_landusetype]
1546
1547
return ids[self.get_landusetypes().are_area[self.ids_landusetype[ids]] == False]
1548
1549
def identify_landuse_from_area(self, ids_fac=None):
1550
"""Determines the landuse of facilities from the landuse of areas in which their are located"""
1551
print 'identify_landuse_from_area', ids_fac
1552
# TODO:
1553
landusetypes = self.get_landusetypes()
1554
ids_area = self.get_ids_area(ids_fac)
1555
ids_build = self.get_ids_building(ids_fac)
1556
1557
for id_area, shape, id_landuse in zip(ids_area, self.shapes[ids_area], self.ids_landusetype[ids_area]):
1558
id_landusetype_fac = landusetypes.get_landusetype_facility_from_area(self.ids_landusetype[id_area])
1559
for id_fac, osmkey, coord in zip(ids_build, self.osmkeys[ids_build], self.centroids[ids_build]):
1560
1561
if osmkey == 'building.yes':
1562
if is_point_in_polygon(coord[:2], np.array(shape, dtype=np.float32)[:, :2], is_use_shapely=IS_SHAPELY):
1563
print ' found id_fac', id_fac, osmkey, 'in id_area', id_area
1564
print ' id_landusetype', self.ids_landusetype[id_fac], 'is_area', landusetypes.are_area[self.ids_landusetype[id_fac]], '->', id_landusetype_fac
1565
self.ids_landusetype[id_fac] = id_landusetype_fac
1566
1567
def update_centroid(self, _id):
1568
self.centroids[_id] = np.mean(self.shapes[_id], 0)
1569
1570
def update_area(self, _id):
1571
if IS_SHAPELY:
1572
self.areas[_id] = find_area(self.shapes[_id])
1573
else:
1574
self.areas[_id] = get_polygonarea_fast(np.array(self.shapes[_id], float)[
1575
:, 0], np.array(self.shapes[_id], float)[:, 1])
1576
1577
def update_capacity(self, id_fac):
1578
self.update_capacities([id_fac])
1579
1580
def update_capacities(self, ids):
1581
ids_fac = self.get_ids_building(ids)
1582
ids_area = self.get_ids_area(ids)
1583
volumes_unit = self.get_facilitytypes().unitvolumes[self.ids_facilitytype[ids_fac]]
1584
1585
self.capacities[ids_fac] = self.areas[ids_fac]*self.heights[ids_fac]/volumes_unit
1586
1587
# here we assume that pure areas do not have capacities
1588
# this will prevent that activities are assigned to areas
1589
# instead of facilities (buildings)
1590
# TODO: problem is for example parks with no buildings
1591
# fixed: parks etc. are areas
1592
self.capacities[ids_area] = 0.0
1593
1594
def get_dists(self, ids_fac_from, ids_fac_to):
1595
"""
1596
Returns centroid to centroid distance from facilities in vector
1597
ids_fac_from to facilities in vector ids_fac_to.
1598
"""
1599
1600
return np.sqrt(np.sum((self.centroids[ids_fac_to]-self.centroids[ids_fac_from])**2))
1601
1602
def identify_closest_edge(self, ids=None, priority_max=5, has_sidewalk=True, n_best=10):
1603
"""
1604
Identifies edge ID and position on this edge that
1605
is closest to the centoid of each facility and the satisfies certain
1606
conditions.
1607
"""
1608
print 'identify_closest_edge'
1609
edges = self.get_edges()
1610
id_ped = self.get_net().modes.get_id_mode('pedestrian')
1611
# select edges...if (edges.priorities[id_edge]<=priority_max) & edges.has_sidewalk(id_edge):
1612
1613
# ids_edge = edges.select_ids((edges.priorities.get_value()<priority_max)\
1614
# & (edges.widths_sidewalk.get_value()>0.0))
1615
accesslevels = edges.get_accesslevels(id_ped)
1616
# edges.make_segment_edge_map()
1617
1618
for id_fac in self.get_ids():
1619
ids_edge, dists = edges.get_closest_edge(self.centroids[id_fac], n_best=n_best, accesslevels=accesslevels)
1620
1621
if len(ids_edge) > 0:
1622
id_edge = ids_edge[0]
1623
1624
# determin position on edeg where edge is closest to centroid
1625
# TODO: solve this faster with precalculated maps!!
1626
xc, yc, zc = self.centroids[id_fac]
1627
shape = edges.shapes[id_edge]
1628
n_segs = len(shape)
1629
1630
d_min = 10.0**8
1631
x_min = 0.0
1632
y_min = 0.0
1633
j_min = 0
1634
p_min = 0.0
1635
pos = 0.0
1636
x1, y1, z1 = shape[0]
1637
edgelength = edges.lengths[id_edge]
1638
for j in xrange(1, n_segs):
1639
x2, y2, z2 = shape[j]
1640
d, xp, yp = shortest_dist(x1, y1, x2, y2, xc, yc)
1641
# print ' x1,y1=(%d,%d)'%(x1,y1),',x2,y2=(%d,%d)'%(x2,y2),',xc,yc=(%d,%d)'%(xc,yc)
1642
# print ' d,x,y=(%d,%d,%d)'%shortest_dist(x1,y1, x2,y2, xc,yc)
1643
if d < d_min:
1644
d_min = d
1645
# print ' **d_min=',d_min,[xp,yp]
1646
x_min = xp
1647
y_min = yp
1648
j_min = j
1649
p_min = pos
1650
# print ' pos',pos,[x2-x1,y2-y1],'p_min',p_min
1651
pos += np.linalg.norm([x2-x1, y2-y1])
1652
x1, y1 = x2, y2
1653
1654
x1, y1, z1 = shape[j_min-1]
1655
pos_min = p_min+np.linalg.norm([x_min-x1, y_min-y1])
1656
# 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)
1657
# print ' pos=%d,p_min=%d,pos_min=%d'%(pos,p_min,pos_min)
1658
1659
if pos_min > edgelength:
1660
pos_min = edgelength
1661
1662
if pos_min < 0:
1663
pos_min = 0
1664
# print ' id_fac,id_edge',id_fac,id_edge,pos_min
1665
self.ids_roadedge_closest[id_fac] = id_edge
1666
self.positions_roadedge_closest[id_fac] = pos_min
1667
1668
def set_shape(self, id_fac, shape):
1669
# print 'set_shape',id_fac,shape
1670
self.shapes[id_fac] = shape
1671
self.update([id_fac])
1672
#self.areas[id_fac] = find_area(shape[:,:2])
1673
#self.centroids[id_fac] =np.mean(shape,0)
1674
1675
def add_polys(self, ids_sumo=[], **kwargs):
1676
"""
1677
Adds a facilities as used on sumo poly xml info
1678
"""
1679
# stuff with landusetype must be done later
1680
return self.add_rows(n=len(ids_sumo), ids_sumo=ids_sumo, **kwargs)
1681
1682
def add_poly(self, id_sumo, id_landusetype=None, osmkey=None, shape=np.array([], np.float32)):
1683
"""
1684
Adds a facility as used on sumo poly xml info
1685
"""
1686
# print 'add_poly',id_sumo,id_landusetype,osmkey
1687
1688
landusetypes = self.get_landusetypes()
1689
1690
if id_landusetype is None:
1691
# make default landuse
1692
id_landusetype = landusetypes.typekeys.get_id_from_index('residential')
1693
1694
if osmkey is None:
1695
# use first filter as key
1696
osmkey = landusetypes.osmfilters[id_landusetype][0]
1697
1698
id_fac = self.add_row(ids_sumo=id_sumo,
1699
ids_landusetype=id_landusetype,
1700
osmkeys=osmkey,
1701
)
1702
self.set_shape(id_fac, shape)
1703
return id_fac
1704
1705
def clear(self):
1706
# self.reset()
1707
self.clear_rows()
1708
1709
def set_shapes(self, ids, vertices):
1710
self.shapes[ids] = vertices
1711
if not hasattr(ids, '__iter__'):
1712
ids = [ids]
1713
self.update(ids)
1714
1715
def import_poly(self, polyfilepath, is_remove_xmlfiles=False, is_clear=True, **kwargs):
1716
print 'import_poly from %s ' % (polyfilepath,)
1717
if is_clear:
1718
self.clear()
1719
# let's read first the offset information, which are in the
1720
# comment area
1721
fd = open(polyfilepath, 'r')
1722
is_comment = False
1723
is_processing = False
1724
offset = self.get_net().get_offset() # default is offset from net
1725
# print ' offset,offset_delta',offset,type(offset)
1726
#offset = np.array([0,0],float)
1727
for line in fd.readlines():
1728
if line.find('<!--') >= 0:
1729
is_comment = True
1730
if is_comment & (line.find('<processing') >= 0):
1731
is_processing = True
1732
if is_processing & (line.find('<offset.x') >= 0):
1733
offset[0] = float(xm.read_keyvalue(line, 'value'))
1734
elif is_processing & (line.find('<offset.y') >= 0):
1735
offset[1] = float(xm.read_keyvalue(line, 'value'))
1736
break
1737
fd.close()
1738
offset_delta = offset - self.get_net().get_offset()
1739
1740
exectime_start = time.clock()
1741
1742
counter = SumoPolyCounter()
1743
parse(polyfilepath, counter)
1744
fastreader = SumoPolyReader(self, counter, offset_delta, **kwargs)
1745
parse(polyfilepath, fastreader)
1746
fastreader.finish()
1747
1748
# update ids_landuse...
1749
# self.update()
1750
1751
# timeit
1752
print ' exec time=', time.clock() - exectime_start
1753
1754
# print ' self.shapes',self.shapes.value
1755
1756
def write_kml(self, fd=None, indent=0):
1757
# <busStop id="busstop4" lane="1/0to2/0_0" startPos="20" endPos="40" lines="100 101"/>
1758
1759
ids_fac = self.get_ids()
1760
for id_fac in ids_fac:
1761
fd.write(xm.begin('Placemark', indent + 2))
1762
fd.write((indent+4)*' '+'<name>%s</name>\n' % id_fac)
1763
fd.write(xm.begin('Polygon', indent + 4))
1764
fd.write((indent+6)*' '+'<extrude>1</extrude>\n' % id_fac)
1765
fd.write((indent+6)*' '+'<altitudeMode>relativeToGround</altitudeMode>\n' % id_fac)
1766
fd.write(xm.begin('outerBoundaryIs', indent + 6))
1767
1768
fd.write(xm.begin('LinearRing', indent + 8))
1769
fd.write(xm.begin('coordinates', indent + 10))
1770
1771
for point in self.shapes[id_fac]:
1772
1773
projection = self.project(point[0], point[1])
1774
fd.write((indent+12)*' '+'%f,%f,%f\n' % (projection[0], projection[1], self.heights[id_fac]))
1775
1776
fd.write(xm.end('coordinates', indent + 10))
1777
fd.write(xm.end('LinearRing', indent + 8))
1778
fd.write(xm.end('outerBoundaryIs', indent + 6))
1779
fd.write(xm.end('Polygon', indent + 4))
1780
fd.write(xm.end('Placemark', indent + 2))
1781
1782
def project(self, x, y):
1783
if self._proj is None:
1784
self._proj, self._offset = self.get_proj_and_offset()
1785
lons, lats = self._proj(x-self._offset[0], y-self._offset[1], inverse=True)
1786
return np.transpose(np.concatenate(([lons], [lats]), axis=0))
1787
1788
def get_proj_and_offset(self):
1789
if self._proj is None:
1790
net = self.parent.parent.net
1791
proj_params = str(net.get_projparams())
1792
# try:
1793
self._proj = pyproj.Proj(proj_params)
1794
# except:
1795
# proj_params ="+proj=utm +zone=32 +ellps=WGS84 +datum=WGS84 +units=m +no_defs"
1796
# self._proj = pyproj.Proj(self.proj_params)
1797
1798
self._offset = net.get_offset()
1799
1800
return self._proj, self._offset
1801
1802
def get_facfilepath(self):
1803
return self.parent.parent.get_rootfilepath()+'.fac.kml'
1804
1805
def export_sumokml(self, filepath=None, encoding='UTF-8'):
1806
"""
1807
Export stops to SUMO stop xml file.
1808
"""
1809
print 'export_sumoxml', filepath, len(self)
1810
if len(self) == 0:
1811
return None
1812
1813
if filepath is None:
1814
filepath = self.get_facfilepath()
1815
1816
try:
1817
fd = open(filepath, 'w')
1818
except:
1819
print 'WARNING in write_obj_to_xml: could not open', filepath
1820
return False
1821
#xmltag, xmltag_item, attrname_id = self.xmltag
1822
fd.write('<?xml version="1.0" encoding="%s"?>\n' % encoding)
1823
fd.write('<kml xmlns="http://www.opengis.net/kml/2.2">\n')
1824
indent = 0
1825
#fd.write(xm.begin('routes xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://sumo.sf.net/xsd/routes_file.xsd"',indent))
1826
1827
fd.write(xm.begin('Document', indent=0))
1828
1829
self.write_kml(fd, indent=0)
1830
1831
fd.write(xm.end('Document', indent=0))
1832
fd.write(xm.end('kml', indent=0))
1833
fd.close()
1834
return filepath
1835
1836
1837
class SumoPolyCounter(handler.ContentHandler):
1838
"""Counts facilities from poly.xml file into facility structure"""
1839
1840
def __init__(self):
1841
self.n_fac = 0
1842
1843
def startElement(self, name, attrs):
1844
# print 'startElement',name,len(attrs)
1845
if name == 'poly':
1846
self.n_fac += 1
1847
1848
1849
class SumoPolyReader(handler.ContentHandler):
1850
"""Reads facilities from poly.xml file into facility structure"""
1851
1852
def __init__(self, facilities, counter, offset_delta, type_default='building.yes', height_default=7.0):
1853
1854
self._type_default = type_default
1855
self._height_default = height_default
1856
self._facilities = facilities
1857
self._ids_landusetype_all = self._facilities.get_landusetypes().get_ids()
1858
self._osmfilters = self._facilities.get_landusetypes().osmfilters
1859
1860
self._ind_fac = -1
1861
self.ids_sumo = np.zeros(counter.n_fac, np.object)
1862
self.ids_landusetype = -1*np.ones(counter.n_fac, np.int32)
1863
self.osmkeys = np.zeros(counter.n_fac, np.object)
1864
self.names = np.zeros(counter.n_fac, np.object)
1865
self.shape = np.zeros(counter.n_fac, np.object)
1866
self.areas = np.zeros(counter.n_fac, np.float32)
1867
self.heights = self._height_default * np.ones(counter.n_fac, np.float32)
1868
self.centroids = np.zeros((counter.n_fac, 3), np.float32)
1869
1870
#self._id_facility = None
1871
self._offset_delta = offset_delta
1872
1873
def startElement(self, name, attrs):
1874
1875
# print 'startElement', name, len(attrs)
1876
if name == 'poly':
1877
self._ind_fac += 1
1878
i = self._ind_fac
1879
1880
osmkey = attrs.get('type', self._type_default)
1881
# print ' id_sumo=',attrs['id'],osmkey
1882
id_landuse = self.get_landuse(osmkey)
1883
if id_landuse >= 0: # land use is interesting
1884
shape = xm.process_shape(attrs.get('shape', ''), offset=self._offset_delta)
1885
shapearray = np.array(shape, np.float32)
1886
# print ' shapearray',shapearray
1887
self.ids_sumo[i] = attrs['id']
1888
self.ids_landusetype[i] = id_landuse
1889
self.osmkeys[i] = osmkey
1890
self.shape[i] = shape
1891
self.areas[i] = find_area(shapearray[:, :2])
1892
self.centroids[i] = np.mean(shapearray, 0)
1893
self.names[i] = ''
1894
1895
# print ' control id_sumo',self.ids_sumo[i]
1896
1897
elif name == 'param':
1898
i = self._ind_fac
1899
if attrs['key'] == 'height':
1900
self.heights[i] = string_to_float(attrs['value'])
1901
elif attrs['key'] == 'name':
1902
self.names[i] = attrs['value']
1903
1904
# color info in this file no longer used as it is defined in
1905
# facility types table
1906
# color = np.array(xm.parse_color(attrs['color']))*0.8,# make em darker!!
1907
1908
def get_landuse(self, osmkey):
1909
keyvec = osmkey.split('.')
1910
len_keyvec = len(keyvec)
1911
print 'get_landuse', len_keyvec, keyvec
1912
#is_match = False
1913
for id_landusetype in self._ids_landusetype_all:
1914
# print ' id_landusetype',id_landusetype
1915
# if fkeys==('building.industrial'): print ' check',facilitytype
1916
1917
# first filter only exact matches before wildcards
1918
for osmfilter in self._osmfilters[id_landusetype]:
1919
# print ' ?osmfiltervec',osmfilter,osmkey==osmfilter
1920
if osmkey == osmfilter: # exact match of filter
1921
print ' exact', osmkey
1922
return id_landusetype
1923
1924
# now check for wildcards
1925
for id_landusetype in self._ids_landusetype_all:
1926
# print ' *id_landusetype',id_landusetype
1927
1928
for osmfilter in self._osmfilters[id_landusetype]:
1929
osmfiltervec = osmfilter.split('.')
1930
# print ' ?osmfiltervec',osmfiltervec,(len(osmfiltervec)==2)&(len_keyvec==2)
1931
if (len(osmfiltervec) == 2) & (len_keyvec == 2):
1932
if osmfiltervec[0] == keyvec[0]:
1933
if osmfiltervec[1] == '*':
1934
print ' *', keyvec[0]
1935
return id_landusetype
1936
return -1
1937
1938
def finish(self):
1939
1940
# print 'write_to_net'
1941
inds_valid = np.flatnonzero(self.ids_landusetype >= 0)
1942
ids_fac = self._facilities.add_polys(
1943
ids_sumo=self.ids_sumo[inds_valid],
1944
ids_landusetype=self.ids_landusetype[inds_valid],
1945
osmkeys=self.osmkeys[inds_valid],
1946
shapes=self.shape[inds_valid],
1947
areas=self.areas[inds_valid],
1948
centroids=self.centroids[inds_valid],
1949
heights=self.heights[inds_valid],
1950
names=self.names[inds_valid]
1951
)
1952
1953
# def characters(self, content):
1954
# if self._id is not None:
1955
# self._currentShape = self._currentShape + content
1956
1957
# def endElement(self, name):
1958
# pass
1959
1960
1961
class Parking(am.ArrayObjman):
1962
def __init__(self, landuse, lanes, **kwargs):
1963
# print 'Parking.__init__',lanes,hasattr(self,'lanes')
1964
self._init_objman(ident='parking', parent=landuse,
1965
name='Parking',
1966
info='Information on private car parking.',
1967
#is_plugin = True,
1968
# **kwargs
1969
)
1970
1971
self._init_attributes(lanes)
1972
1973
def _init_attributes(self, lanes=None):
1974
# print 'Parkin._init_attributes',lanes,hasattr(self,'lanes'),hasattr(self,'ids_lane')
1975
if lanes is None:
1976
# upgrade call
1977
# lanes exists already as link
1978
lanes = self.get_lanes()
1979
1980
# print ' lanes',lanes
1981
# --------------------------------------------------------------------
1982
# misc params...
1983
1984
# these are options for assignment procedure!!
1985
# self.add(AttrConf( 'length_noparking', kwargs.get('length_noparking',20.0),
1986
# groupnames = ['options'],
1987
# perm='wr',
1988
# unit = 'm',
1989
# name = 'Min Length',
1990
# info = 'Minimum edge length for assigning on-road parking space.' ,
1991
# #xmltag = 'pos',
1992
# ))
1993
#
1994
# self.add(AttrConf( 'length_space', kwargs.get('length_space',20.0),
1995
# groupnames = ['options'],
1996
# perm='wr',
1997
# unit = 'm',
1998
# name = 'Lot length',
1999
# info = 'Length of a standard parking lot.' ,
2000
# #xmltag = 'pos',
2001
# ))
2002
2003
self.add_col(am.IdsArrayConf('ids_lane', lanes,
2004
name='ID Lane',
2005
info='ID of lane for this parking position. ',
2006
))
2007
2008
self.add_col(am.ArrayConf('positions', 0.0,
2009
dtype=np.float32,
2010
perm='r',
2011
name='Pos',
2012
unit='m',
2013
info="Position on lane for this parking.",
2014
))
2015
2016
self.add_col(am.ArrayConf('lengths', 0.0,
2017
dtype=np.float32,
2018
#choices = OPTIONMAP_POS_DEPARTURE,
2019
perm='r',
2020
name='Length',
2021
unit='m',
2022
info="Length of parking lot in edge direction.",
2023
))
2024
2025
self.add_col(am.ArrayConf('angles', 0.0,
2026
dtype=np.float32,
2027
perm='rw',
2028
name='Angle',
2029
unit='deg',
2030
info="Parking angle with respect to lane direction.",
2031
))
2032
2033
self.add_col(am.ArrayConf('vertices', np.zeros((2, 3), dtype=np.float32),
2034
dtype=np.float32,
2035
groupnames=['_private'],
2036
perm='r',
2037
name='Coords',
2038
unit='m',
2039
info="Start and end vertices of right side of parking space.",
2040
))
2041
2042
self.add_col(am.ArrayConf('numbers_booking', 0, # ???
2043
dtype=np.int32,
2044
perm='r',
2045
name='booked',
2046
info="Number of vehicles booked for this parking.",
2047
))
2048
2049
# self.add_col(am.ArrayConf( 'durations', 0.0,# ???
2050
# dtype=np.float32,
2051
# perm='r',
2052
# name = 'Parking duration',
2053
# unit = 's',
2054
# info = "Default duration of car parking.",
2055
# ))
2056
2057
self.add(cm.ObjConf(lanes, is_child=False, groups=['_private']))
2058
2059
self.add(cm.ObjConf(lanes.parent.edges, is_child=False, groups=['_private']))
2060
2061
def get_edges(self):
2062
return self.edges.get_value()
2063
2064
def get_lanes(self):
2065
return self.lanes.get_value()
2066
2067
def link_vehiclefleet(self, vehicles):
2068
"""
2069
Links to table with vehicle info.
2070
"""
2071
self.add_col(am.IdsArrayConf('ids_bookedveh', vehicles,
2072
name='ID booked veh',
2073
info='ID of vehicle which has booked this parking position.',
2074
))
2075
2076
def update_netoffset(self, deltaoffset):
2077
"""
2078
Called when network offset has changed.
2079
Children may need to adjust their coordinates.
2080
"""
2081
pass
2082
2083
def get_parkinglane_from_edge(self, id_edge, id_mode, length_min=15.0, priority_max=8, n_freelanes_min=1):
2084
"""
2085
Check if edge can have on-road parking.
2086
Returns lane ID if parking is possible and -1 otherwise.
2087
"""
2088
edges = self.edges.get_value()
2089
lanes = self.lanes.get_value()
2090
id_mode_ped = edges.parent.modes.get_id_mode('pedestrian')
2091
# check access
2092
ids_lane = edges.ids_lanes[id_edge]
2093
edgelength = edges.lengths[id_edge]
2094
2095
if (len(ids_lane) >= 2):
2096
if (lanes.get_accesslevel([ids_lane[0]], id_mode_ped) > -1) & (lanes.get_accesslevel([ids_lane[1]], id_mode) > -1):
2097
# check size
2098
# laneindex =
2099
# print 'get_parkinglane_from_edge',id_edge, id_mode,priority_max,length_min
2100
# print ' check',(edges.priorities[id_edge]<=priority_max),(edges.lengths[id_edge]>length_min),(edges.widths_sidewalk[id_edge]>-1)
2101
2102
if (edges.priorities[id_edge] <= priority_max) & (edgelength > length_min) & (edges.widths_sidewalk[id_edge] > 0):
2103
2104
laneindex = 1
2105
# print ' found',laneindex,edges.nums_lanes[id_edge]-laneindex > n_freelanes_min
2106
if (len(ids_lane)-laneindex >= n_freelanes_min):
2107
return ids_lane[laneindex]
2108
else:
2109
return -1
2110
else:
2111
return -1
2112
else:
2113
return -1
2114
2115
return -1 # no parking possible by default
2116
2117
def get_edge_pos_parking(self, id_parking):
2118
lanes = self.lanes.get_value()
2119
return lanes.ids_edge[self.ids_lane[id_parking]], self.positions[id_parking]
2120
2121
# def get_edge_pos_parking(self, id_parking):
2122
# """
2123
# Retuens edge and position of parking with id_parking
2124
# """
2125
# ind = self.parking.get_ind(id_parking)
2126
#
2127
# return self.edges.get_value()(self.id_edge_parking[ind]),self.pos_edge_parking[ind]
2128
2129
def make_parking(self, id_mode=MODES['passenger'],
2130
length_min=42.0, length_noparking=15.0,
2131
length_lot=6.0, angle=0.0,
2132
is_clear=True,
2133
logger=None,
2134
**kwargs):
2135
print 'make_parking'
2136
if is_clear:
2137
self.clear()
2138
edges = self.edges.get_value()
2139
lanes = self.lanes.get_value()
2140
n_parking = 0
2141
ids_parking = []
2142
ids_lane_current = self.ids_lane.get_value().copy()
2143
ii = 0.0
2144
n_edges = len(edges.get_ids())
2145
for id_edge in edges.get_ids():
2146
ii += 1
2147
if logger is not None:
2148
logger.progress(int(ii/n_edges*100))
2149
# check if edge is suitable...
2150
# print ' id_edge,length,n_lanes,',id_edge
2151
id_lane = self.get_parkinglane_from_edge(id_edge, id_mode, length_min, **kwargs)
2152
2153
is_eligible = id_lane >= 0
2154
if not is_clear:
2155
if id_lane not in ids_lane_current:
2156
is_eligible = False
2157
2158
if is_eligible:
2159
n_spaces = int((edges.lengths[id_edge]-2*length_noparking)/length_lot)
2160
# print ' create',id_edge,lanes.indexes[id_lane],edges.lengths[id_edge],n_spaces
2161
# print ' delta',lanes.shapes[id_lane][0]-lanes.shapes[id_lane][-1]
2162
pos_offset = length_noparking
2163
pos = pos_offset
2164
if n_spaces > 0:
2165
for i in xrange(n_spaces):
2166
#id_park = self.suggest_id()
2167
# print ' pos=',pos,pos/edges.lengths[id_edge]
2168
2169
# print ' vertices',get_vec_on_polyline_from_pos(lanes.shapes[id_lane],pos, length_lot, angle = angle)
2170
n_parking += 1
2171
2172
id_park = self.add_row(ids_lane=id_lane,
2173
positions=pos,
2174
lengths=length_lot,
2175
angles=angle,
2176
vertices=get_vec_on_polyline_from_pos(
2177
lanes.shapes[id_lane], pos, length_lot-0.5, angle=angle)
2178
)
2179
# print ' created id_park,pos', id_park,pos#,get_coord_on_polyline_from_pos(lanes.shapes[id_lane],pos),lanes.shapes[id_lane]
2180
ids_parking.append(id_park)
2181
pos = pos_offset+(i+1)*length_lot
2182
2183
print ' created %d parking spaces' % n_parking
2184
return ids_parking
2185
2186
def clear_booking(self):
2187
self.numbers_booking.reset()
2188
if hasattr(self, 'ids_bookedveh'):
2189
self.ids_bookedveh.reset()
2190
2191
def get_closest_parking(self, id_veh, coord, c_spread=2.0):
2192
"""
2193
Returns parking space for id_veh as close as possible to coord.
2194
"""
2195
2196
#inds_person = self.persons.get_inds(ids_person)
2197
print 'get_closest_parking'
2198
ind_parking_closest = self.get_inds()[np.argmin(
2199
np.sum((coord-self.vertices.value[:, 1, :])**2, 1) + c_spread*lengths*self.lengths.get_value())]
2200
self.numbers_booking.get_value()[ind_parking_closest] += 1
2201
return self.get_ids(ind_parking_closest), ind_parking_closest
2202
2203
def get_closest_parkings(self, ids_veh, ids_mode, coords, dists_walk_max, c_spread=2.0,
2204
n_retrials=20, id_mode_fallback=None):
2205
"""
2206
Returns parking space for each vehicle in ids_veh as close as possible to coords.
2207
"""
2208
2209
# Used by virtualpop
2210
2211
lanes = self.ids_lane.get_linktab()
2212
ids_lane = self.ids_lane.get_value()
2213
#inds_person = self.persons.get_inds(ids_person)
2214
n = len(ids_veh)
2215
2216
# print 'get_closest_parking',n,len(self),'n_retrials',n_retrials
2217
if len(self) == 0:
2218
print 'WARNING in get_closest_parkings: there is no parking.'
2219
return [], []
2220
2221
#parking = self.get_landuse().parking
2222
#inds_parking = parking.get_inds()
2223
coord_parking = self.vertices.value[:, 1, :]
2224
# print ' coord_parking',coord_parking
2225
numbers_booking = self.numbers_booking.get_value()
2226
lengths = self.lengths.get_value()
2227
inds_vehparking = np.zeros(n, int)
2228
are_fallback = np.zeros(n, bool)
2229
2230
#inds_parking_avail = np.flatnonzero( self.ids_bookedveh.value == -1).tolist()
2231
inds_parking_avail = self.get_inds().copy()
2232
2233
#ids_veh = np.zeros(n,object)
2234
i = 0
2235
for id_veh, id_mode, coord, dist_walk_max_sq in zip(ids_veh, ids_mode, coords, dists_walk_max**2):
2236
# print ' search parking for id_veh',id_veh
2237
# print ' landuse.id_bookedveh_parking',landuse.id_bookedveh_parking
2238
#
2239
2240
# print ' inds_parking_avail',inds_parking_avail
2241
# print ' dists',np.sum((coord-coord_parking[inds_parking_avail])**2,1),np.argmin(np.sum((coord-coord_parking[inds_parking_avail])**2,1))
2242
is_fallback = False
2243
penalties_inaccessible = np.ones(len(numbers_booking), dtype=np.float32)
2244
n_search = n_retrials
2245
is_search = True
2246
while (n_search > 0) & is_search:
2247
dists = np.sum((coord-coord_parking)**2, 1)
2248
ind_parking_closest = inds_parking_avail[np.argmin(
2249
dists + c_spread*lengths*numbers_booking*penalties_inaccessible)]
2250
# print ' ind_parking_closest,n_avail',ind_parking_closest,len(inds_parking_avail),'n_search',n_search,'is_search',is_search
2251
is_search = (not (lanes.get_accesslevel([ids_lane[ind_parking_closest]], id_mode) >= 0))\
2252
| (dists[ind_parking_closest] > dist_walk_max_sq)
2253
penalties_inaccessible[ind_parking_closest] = np.inf # prevent reselection of this parking
2254
n_search -= 1
2255
# print ' n_search',n_search,'is_search',is_search
2256
2257
# print ' done with id_mode is_search',is_search
2258
if is_search & (id_mode_fallback is not None):
2259
# search mode means no parking has bee found for ordinary mode
2260
# now try with fallback mode
2261
2262
is_fallback = True
2263
penalties_inaccessible = np.ones(len(numbers_booking), dtype=np.float32)
2264
n_search = n_retrials
2265
while (n_search > 0) & is_search:
2266
dists = np.sum((coord-coord_parking)**2, 1)
2267
ind_parking_closest = inds_parking_avail[np.argmin(
2268
dists + c_spread*lengths*numbers_booking*penalties_inaccessible)]
2269
# print ' ind_parking_closest,n_avail',ind_parking_closest,len(inds_parking_avail)
2270
is_search = (not (lanes.get_accesslevel([ids_lane[ind_parking_closest]], id_mode_fallback) >= 0))\
2271
| (dists[ind_parking_closest] > dist_walk_max_sq)
2272
penalties_inaccessible[ind_parking_closest] = np.inf # prevent reselection of this parking
2273
n_search -= 1
2274
# print ' fallback n_search',n_search,'is_search',is_search
2275
2276
if is_search:
2277
print 'WARNING: inaccessible parking for id_veh', id_veh, 'is_fallback', is_fallback
2278
print ' dist=%.1f' % (np.sqrt(dists[ind_parking_closest])), 'id_lane', ids_lane[ind_parking_closest], 'al', lanes.get_accesslevel([ids_lane[ind_parking_closest]], id_mode_fallback)
2279
2280
inds_vehparking[i] = ind_parking_closest
2281
are_fallback[i] = is_fallback
2282
# print ' coords_veh',coord
2283
# print ' coord_park',coord_parking[ind_parking_closest]
2284
numbers_booking[ind_parking_closest] += 1
2285
2286
#id_parking = self.get_ids([ind_parking_closest])
2287
#id_edge, pos = self.get_edge_pos_parking(id_parking)
2288
# 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)))
2289
# ids_bookedveh[ind_parking_closest]=id_veh # occupy parking
2290
# print ' id_edge, pos',id_edge, pos
2291
# inds_parking_avail.remove(ind_parking_closest)
2292
i += 1
2293
2294
# print ' inds_vehparking', inds_vehparking
2295
# print ' ids_vehparking', self.get_ids(inds_vehparking)
2296
# print ' ids_veh',ids_veh
2297
#self.ids_bookedveh.value[inds_vehparking] = ids_veh
2298
# self.ids_bookedveh.[ids_parking] =ids_bookedveh
2299
return self.get_ids(inds_vehparking), are_fallback
2300
2301
def assign_parking(self, ids_veh, coords, is_overbook=False):
2302
"""
2303
Assigns a parking space to each vehicle as close as possible to coords.
2304
Only one vehicle can be assigned to a parking space.
2305
"""
2306
2307
#inds_person = self.persons.get_inds(ids_person)
2308
n = len(ids_veh)
2309
# print 'assign_parking',n
2310
2311
#parking = self.get_landuse().parking
2312
#inds_parking = parking.get_inds()
2313
coord_parking = self.vertices.value[:, 1, :]
2314
2315
inds_vehparking = np.zeros(n, int)
2316
2317
inds_parking_avail = np.flatnonzero(self.ids_bookedveh.value == -1).tolist()
2318
2319
#ids_veh = np.zeros(n,object)
2320
i = 0
2321
for id_veh, coord in zip(ids_veh, coords):
2322
# print '\n id_veh,coord',id_veh,coord
2323
# print ' landuse.id_bookedveh_parking',landuse.id_bookedveh_parking
2324
#
2325
2326
# print ' inds_parking_avail',inds_parking_avail
2327
# print ' dists',np.sum((coord-coord_parking[inds_parking_avail])**2,1),np.argmin(np.sum((coord-coord_parking[inds_parking_avail])**2,1))
2328
ind_parking_closest = inds_parking_avail[np.argmin(np.sum((coord-coord_parking[inds_parking_avail])**2, 1))]
2329
# print ' ind_parking_closest,n_avail',ind_parking_closest,len(inds_parking_avail)
2330
inds_vehparking[i] = ind_parking_closest
2331
# 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)))
2332
# ids_bookedveh[ind_parking_closest]=id_veh # occupy parking
2333
2334
inds_parking_avail.remove(ind_parking_closest)
2335
i += 1
2336
2337
# print ' inds_vehparking', inds_vehparking
2338
# print ' ids_vehparking', self.get_ids(inds_vehparking)
2339
# print ' ids_veh',ids_veh
2340
self.ids_bookedveh.value[inds_vehparking] = ids_veh
2341
# self.ids_bookedveh.[ids_parking] =ids_bookedveh
2342
return self.get_ids(inds_vehparking), inds_vehparking
2343
2344
2345
class Landuse(cm.BaseObjman):
2346
def __init__(self, scenario=None, net=None, **kwargs):
2347
self._init_objman(ident='landuse', parent=scenario, name='Landuse', **kwargs)
2348
attrsman = self.set_attrsman(cm.Attrsman(self))
2349
2350
if scenario is not None:
2351
net = scenario.net
2352
# self.net = attrsman.add( cm.ObjConf( net, is_child = False ) )# link only
2353
2354
self.landusetypes = attrsman.add(cm.ObjConf(LanduseTypes(self)))
2355
self.zones = attrsman.add(cm.ObjConf(Zones(self, net.edges)))
2356
self.facilities = attrsman.add(cm.ObjConf(Facilities(self, self.landusetypes, self.zones, net=net)))
2357
self.parking = attrsman.add(cm.ObjConf(Parking(self, net.lanes)))
2358
self.maps = attrsman.add(cm.ObjConf(maps.Maps(self)))
2359
2360
def update_netoffset(self, deltaoffset):
2361
"""
2362
Called when network offset has changed.
2363
Children may need to adjust theur coordinates.
2364
"""
2365
self.zones.update_netoffset(deltaoffset)
2366
self.facilities.update_netoffset(deltaoffset)
2367
self.parking.update_netoffset(deltaoffset)
2368
self.maps.update_netoffset(deltaoffset)
2369
2370
def get_net(self):
2371
# parent of landuse must be scenario
2372
if self.parent is not None:
2373
return self.parent.net
2374
else:
2375
return None
2376
2377
def export_polyxml(self, filepath=None, encoding='UTF-8', delta=np.zeros(3, dtype=np.float32)):
2378
"""
2379
Export landuse facilities to SUMO poly.xml file.
2380
"""
2381
if len(self.facilities) == 0:
2382
return None
2383
2384
if filepath is None:
2385
if self.parent is not None:
2386
filepath = self.get_filepath()
2387
else:
2388
filepath = os.path.join(os.getcwd(), 'landuse.poly.xml')
2389
2390
print 'export_polyxml', filepath
2391
try:
2392
fd = open(filepath, 'w')
2393
except:
2394
print 'WARNING in export_poly_xml: could not open', filepath
2395
return None
2396
2397
#xmltag, xmltag_item, attrname_id = self.xmltag
2398
xmltag_poly = 'additional'
2399
fd.write('<?xml version="1.0" encoding="%s"?>\n' % encoding)
2400
fd.write(xm.begin(xmltag_poly))
2401
indent = 2
2402
2403
fd.write(xm.start('location', indent+2))
2404
# print ' groups:',self.parent.net.get_attrsman().get_groups()
2405
for attrconfig in self.parent.net.get_attrsman().get_group('location'):
2406
# print ' locationconfig',attrconfig.attrname
2407
if attrconfig.attrname == '_boundaries':
2408
delta_bb = np.zeros(4, dtype=np.float32)
2409
delta_bb[0:2] = delta[:2]
2410
delta_bb[2:4] = delta[:2]
2411
fd.write(xm.arr('convBoundary', attrconfig.get_value()-delta_bb))
2412
else:
2413
attrconfig.write_xml(fd)
2414
fd.write(xm.stopit())
2415
2416
self.facilities.write_xml(fd, indent=indent+2, is_print_begin_end=False, delta=delta)
2417
2418
fd.write(xm.end(xmltag_poly))
2419
fd.close()
2420
return filepath
2421
2422
def get_filepath(self):
2423
return self.parent.get_rootfilepath() + '.poly.xml'
2424
2425
def import_polyxml(self, rootname=None, dirname='', filepath=None, is_clear=True, **kwargs):
2426
if filepath is None:
2427
if rootname is not None:
2428
filepath = os.path.join(dirname, rootname+'.poly.xml')
2429
else:
2430
filepath = self.get_filepath()
2431
2432
if os.path.isfile(filepath):
2433
self.facilities.import_poly(filepath, is_clear=is_clear, **kwargs)
2434
2435
else:
2436
self.get_logger().w('import_xml: files not found:'+filepath, key='message')
2437
2438
#
2439
# here may be other relevant imports
2440
#
2441
2442
2443
class FacilityGenerator(Process):
2444
def __init__(self, ident='facilitygenerator', facilities=None, logger=None, **kwargs):
2445
print 'FacilityGenerator.__init__'
2446
2447
# TODO: let this be independent, link to it or child??
2448
2449
self._init_common(ident,
2450
parent=facilities,
2451
name='Facility Generator',
2452
logger=logger,
2453
info='Generates facilities (buildigs, factories, parks, etc.) in a given street network.',
2454
)
2455
2456
attrsman = self.set_attrsman(cm.Attrsman(self))
2457
2458
# make for each possible pattern a field for prob
2459
2460
self.edgelength_min = attrsman.add(cm.AttrConf('edgelength_min', kwargs.get('edgelength_min', 50.0),
2461
groupnames=['options'],
2462
perm='rw',
2463
name='Minimum edge length',
2464
unit='m',
2465
info="""Minimum edge length for which houses are generated.""",
2466
))
2467
2468
self.priority_max = attrsman.add(cm.AttrConf('priority_max', kwargs.get('priority_max', 7),
2469
groupnames=['options'],
2470
perm='rw',
2471
name='Max. priority',
2472
info="""Maximum edge priority where facilities will be created.""",
2473
))
2474
2475
self.height_max = attrsman.add(cm.AttrConf('height_max', kwargs.get('height_max', 20.0),
2476
groupnames=['options'],
2477
perm='rw',
2478
unit='m',
2479
name='Max facility height',
2480
info="""Maximum height of facilities.""",
2481
))
2482
2483
self.capacity_max = attrsman.add(cm.AttrConf('capacity_max', kwargs.get('capacity_max', 1000),
2484
groupnames=['options'],
2485
perm='rw',
2486
name='Max. facility capacity',
2487
info="""Maximum capacity of a facility. Capacity is the number of adulds living in a house or working in a factory.""",
2488
))
2489
2490
self.n_retry = attrsman.add(cm.AttrConf('n_retry', kwargs.get('n_retry', 5),
2491
groupnames=['options'],
2492
perm='rw',
2493
name='Retry number',
2494
info="""Number of times the algorithm is trying to fit a facility in a road-gap.""",
2495
))
2496
2497
# self.id_facilitytype = attrsman.add(cm.AttrConf( 'id_facilitytype',kwargs.get('id_facilitytype',1),
2498
# groupnames = ['options'],
2499
# perm='rw',
2500
# choices = self.parent.facilities.facilitytypes.get_value().names.get_indexmap(),
2501
# name = 'Facility type',
2502
# info = """Facility type to be generated.""",
2503
# ))
2504
2505
def do(self):
2506
print self.get_name()+'.do'
2507
# links
2508
facilities = self.parent
2509
net = facilities.parent.get_net()
2510
edges = net.edges
2511
nodes = net.nodes
2512
#self._edges = edges
2513
2514
#self._segvertices = edges.get_segvertices_xy()
2515
x1, y1, x2, y2 = edges.get_segvertices_xy()
2516
2517
logger = self.get_logger()
2518
2519
ids_edge = edges.select_ids((edges.widths_sidewalk.get_value() > 0)
2520
& (edges.lengths.get_value() > self.edgelength_min)
2521
& (edges.priorities.get_value() < self.priority_max)
2522
)
2523
facilitytypes = facilities.facilitytypes.get_value()
2524
2525
# here we can make a selection
2526
facilitytypeobjs = facilitytypes.typeobjects[facilitytypes.get_ids()]
2527
2528
# print ' facilitytypes, facilitytypeobjs',facilitytypes,facilitytypeobjs
2529
n_factypes = len(facilitytypes)
2530
n_fac = 0
2531
n_edges = len(ids_edge)
2532
#logger.w('Add facilities to %d edges')
2533
# print ' eligible edges =',ids_edge
2534
i = 0.0
2535
for id_edge, edgelength, id_fromnode, id_tonode, shape, edgewidth\
2536
in zip(ids_edge, edges.lengths[ids_edge],
2537
edges.ids_fromnode[ids_edge],
2538
edges.ids_tonode[ids_edge],
2539
edges.shapes[ids_edge],
2540
edges.widths[ids_edge],
2541
):
2542
pos = 5.0
2543
# print ' Build at edge',id_edge,edgelength
2544
#logger.w('Add facilities to %d edges')
2545
i += 1
2546
logger.progress(i/n_edges*100)
2547
2548
# identify opposite edge, which needs to be excluded
2549
# from bulding overlapping check
2550
if (nodes.ids_incoming[id_fromnode] is not None)\
2551
& (nodes.ids_outgoing[id_tonode] is not None):
2552
ids_incoming_fomnode = set(nodes.ids_incoming[id_fromnode])
2553
ids_outgoing_tonode = set(nodes.ids_outgoing[id_tonode])
2554
2555
id_edge_opp_set = ids_incoming_fomnode.intersection(ids_outgoing_tonode)
2556
if len(id_edge_opp_set) > 0:
2557
id_edge_opp = id_edge_opp_set.pop()
2558
inds_seg_opp = edges.get_inds_seg_from_id_edge(id_edge_opp)
2559
else:
2560
# print ' no edge in opposite direction'
2561
id_edge_opp = -1
2562
inds_seg_opp = None
2563
else:
2564
id_edge_opp = -1
2565
inds_seg_opp = None
2566
#ids_tonode_outgoing = edges.ids_tonode[nodes.ids_outgoing[id_tonode]]
2567
# net.get_ids_edge_from_inds_seg(inds_seg)
2568
# net.get_inds_seg_from_id_edge(id_edge)
2569
2570
while pos < edgelength:
2571
facilitytype = facilitytypeobjs[0] # could be according to statistics
2572
# print ' next position',pos
2573
n_trials = self.n_retry
2574
is_success = False
2575
while (n_trials > 0) & (not is_success):
2576
length_fac = random.uniform(facilitytype.length_min, facilitytype.length_max)
2577
width_fac = random.uniform(facilitytype.width_min, facilitytype.width_max)
2578
2579
# fix from to positions
2580
pos11 = pos
2581
pos21 = pos + length_fac
2582
if pos21 < edgelength:
2583
# print ' try place',n_trials,facilitytype,'id_edge',id_edge,pos11,pos21,edgelength
2584
2585
coord11, angle = get_coord_angle_on_polyline_from_pos(shape, pos11)
2586
dxn = np.cos(angle-np.pi/2)
2587
dyn = np.sin(angle-np.pi/2)
2588
coord12 = [coord11[0]+width_fac*dxn, coord11[1]+width_fac*dyn, coord11[2]]
2589
2590
coord21, angle = get_coord_angle_on_polyline_from_pos(shape, pos21)
2591
dxn = np.cos(angle-np.pi/2)
2592
dyn = np.sin(angle-np.pi/2)
2593
coord22 = [coord21[0]+width_fac*dxn, coord21[1]+width_fac*dyn, coord21[2]]
2594
2595
id_edge1 = edges.get_ids_edge_from_inds_seg(self.get_segind_closest_edge(
2596
coord12, x1, y1, x2, y2, inds_seg_exclude=inds_seg_opp))
2597
2598
#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))
2599
# print ' id_edge,id_edge1,id_edge2',id_edge,id_edge1,id_edge2
2600
# print ' shape =',np.array([coord11, coord12, coord22, coord21,], dtype = np.float32)
2601
if id_edge1 == id_edge:
2602
id_edge2 = edges.get_ids_edge_from_inds_seg(self.get_segind_closest_edge(
2603
coord22, x1, y1, x2, y2, inds_seg_exclude=inds_seg_opp))
2604
2605
if id_edge2 == id_edge:
2606
id_fac = facilities.generate(facilitytype,
2607
offset=coord11, # offset
2608
length=length_fac,
2609
width=width_fac,
2610
#bbox = [coord11, coord12, coord22, coord21,],
2611
id_landusetype=None,
2612
angle=angle,
2613
pos_edge=pos11,
2614
capacity=self.capacity_max, # could be function of dist to center/pop
2615
height_max=self.height_max, # could be function of dist to center
2616
id_edge=id_edge,
2617
width_edge=edgewidth,
2618
)
2619
2620
if id_fac != -1:
2621
# print ' ****generation successful id_fac=',id_fac
2622
is_success = True
2623
n_fac += 1
2624
2625
n_trials -= 1
2626
2627
pos = pos21
2628
# print ' update with pos',pos
2629
# generate a parallel shape with distance width_fac
2630
#angles_perb = get_angles_perpendicular(shape)
2631
#dxn = np.cos(angles_perb)
2632
#dyn = np.sin(angles_perb)
2633
#shape2 = np.zeros(shape.shape, np.float32)
2634
#shape2[:,0] = dxn*width_fac + shape[:,0]
2635
#shape2[:,1] = dyn*width_fac + shape[:,1]
2636
#shape2[:,2] = shape[:,2]
2637
2638
# check if positions on parallel shape are closest to
2639
# this edge or closer to another edge
2640
print ' Done, generated %d facilities' % n_fac
2641
return True
2642
2643
def get_segind_closest_edge(self, p, x1, y1, x2, y2, inds_seg_exclude=None):
2644
d2 = get_dist_point_to_segs(p[0:2], x1, y1, x2, y2, is_ending=True)
2645
if inds_seg_exclude is not None:
2646
d2[inds_seg_exclude] = np.inf
2647
# print ' min(d2)=',np.min(d2),'argmin=',np.argmin(d2),self.get_ids(self._edgeinds[np.argmin(d2)])
2648
return np.argmin(d2)
2649
2650
2651
class ParkingGenerator(Process):
2652
def __init__(self, ident='parkinggenerator', parking=None, logger=None, **kwargs):
2653
print 'ParkingGenerator.__init__'
2654
2655
# TODO: let this be independent, link to it or child??
2656
2657
self._init_common(ident,
2658
parent=parking,
2659
name='On Road parking generator',
2660
logger=logger,
2661
info='Generates on road parking.',
2662
)
2663
2664
attrsman = self.set_attrsman(cm.Attrsman(self))
2665
scenario = parking.parent.parent
2666
2667
self.id_mode = attrsman.add(cm.AttrConf('id_mode', kwargs.get('id_mode', MODES['passenger']),
2668
groupnames=['options'],
2669
choices=scenario.net.modes.names.get_indexmap(),
2670
perm='rw',
2671
name='Mode ID',
2672
info="""Mode of parked vehicles. This is to select lanes which must be accessible for this mode.""",
2673
))
2674
2675
self.length_min = attrsman.add(cm.AttrConf('length_min', kwargs.get('length_min', 42.0),
2676
groupnames=['options'],
2677
perm='rw',
2678
unit='m',
2679
name='Min. edge length',
2680
info="""Minimum edge length in order to qualify for parking.""",
2681
))
2682
2683
self.length_noparking = attrsman.add(cm.AttrConf('length_noparking', kwargs.get('length_noparking', 15.0),
2684
groupnames=['options'],
2685
perm='rw',
2686
unit='m',
2687
name='No parking length',
2688
info="""Length from junction to the first or last parking on an edge.""",
2689
))
2690
2691
self.length_lot = attrsman.add(cm.AttrConf('length_lot', kwargs.get('length_lot', 6.0),
2692
groupnames=['options'],
2693
perm='rw',
2694
unit='m',
2695
name='Lot length',
2696
info="""Length of a single parking lot.""",
2697
))
2698
2699
self.angle = attrsman.add(cm.AttrConf('angle', kwargs.get('angle', 0.0),
2700
groupnames=['options'],
2701
perm='rw',
2702
name='Angle',
2703
info="""Angle of parking with respect ti lane direction. Currently only 0.0 is possible.""",
2704
))
2705
2706
self.priority_max = attrsman.add(cm.AttrConf('priority_max', kwargs.get('priority_max', 7),
2707
groupnames=['options'],
2708
perm='rw',
2709
name='Max. priority',
2710
info="""Maximum edge priority where parkings will be created.""",
2711
))
2712
2713
self.n_freelanes_min = attrsman.add(cm.AttrConf('n_freelanes_min', kwargs.get('n_freelanes_min', 1),
2714
groupnames=['options'],
2715
perm='rw',
2716
name='Min. free lanes',
2717
info="""Minimum number of free lanes on the edge. These is the minimum number of lanes excluding the parking lane.""",
2718
))
2719
self.is_clear = attrsman.add(cm.AttrConf('is_clear', kwargs.get('is_clear', True),
2720
groupnames=['options'],
2721
perm='rw',
2722
name='Clear',
2723
info="""Clear previous parking areas from ntework.""",
2724
))
2725
2726
def do(self):
2727
print self.get_name()+'.do'
2728
# links
2729
# print ' self.id_mode',self.id_mode
2730
# print ' self.get_kwoptions()',self.get_kwoptions()
2731
logger = self.get_logger()
2732
2733
self.parent.make_parking(logger=logger, **self.get_kwoptions())
2734
return True
2735
2736
2737
class OsmPolyImporter(CmlMixin, Process):
2738
def __init__(self, landuse=None,
2739
osmfilepaths=None,
2740
typefilepath=None,
2741
polyfilepath=None,
2742
projparams=None,
2743
offset_x=None,
2744
offset_y=None,
2745
is_keep_full_type=True,
2746
is_import_all_attributes=True,
2747
is_use_name_for_id=False,
2748
polytypefilepath='',
2749
is_clean_osmfile=True,
2750
is_merge=False,
2751
logger=None, **kwargs):
2752
print 'OsmPolyImporter.__init__', landuse, landuse.parent.get_rootfilename()
2753
self._init_common('osmpolyimporter', name='OSM Poly import',
2754
logger=logger,
2755
info='Converts a OSM file to a SUMO Poly file and read facilities into scenario.',
2756
)
2757
if landuse is None:
2758
self._landuse = Landuse()
2759
else:
2760
self._landuse = landuse
2761
2762
self.init_cml('polyconvert') # pass main shell command
2763
2764
if landuse.parent is not None:
2765
scenario = landuse.parent
2766
rootname = scenario.get_rootfilename()
2767
rootdirpath = scenario.get_workdirpath()
2768
if hasattr(scenario, 'net'):
2769
if projparams is None:
2770
projparams = scenario.net.get_projparams()
2771
if (offset_x is None) & (offset_y is None):
2772
offset_x, offset_y = scenario.net.get_offset()
2773
else:
2774
rootname = landuse.get_ident()
2775
rootdirpath = os.getcwd()
2776
2777
if polyfilepath is None:
2778
polyfilepath = os.path.join(rootdirpath, rootname+'.poly.xml')
2779
2780
if osmfilepaths is None:
2781
osmfilepaths = os.path.join(rootdirpath, rootname+'.osm.xml')
2782
2783
if typefilepath is None:
2784
if 'SUMO_HOME' in os.environ:
2785
typemapdir = os.path.join(os.environ['SUMO_HOME'], 'data', 'typemap')
2786
typefilepath = os.path.join(typemapdir, 'osmPolyconvert.typ.xml')
2787
else:
2788
print("No poly typemaps found. Please declare environment variable 'SUMO_HOME'")
2789
typefilepath = ''
2790
2791
attrsman = self.get_attrsman()
2792
2793
self.rootdirpath = rootdirpath
2794
2795
self.rootname = rootname
2796
2797
self.add_option('osmfilepaths', osmfilepaths,
2798
groupnames=['options'],
2799
cml='--osm-files',
2800
perm='rw',
2801
name='OSM files',
2802
wildcards='OSM XML files (*.osm)|*.osm*',
2803
metatype='filepaths',
2804
info='Openstreetmap files to be imported.',
2805
)
2806
2807
self.is_merge = attrsman.add(cm.AttrConf('is_merge', is_merge,
2808
groupnames=['options'],
2809
perm='rw',
2810
name='Merge',
2811
info='If set, imported polygons are merged with existing.',
2812
))
2813
2814
self.is_clean_osmfile = attrsman.add(cm.AttrConf('is_clean_osmfile', is_clean_osmfile,
2815
groupnames=['options'],
2816
perm='rw',
2817
name='Clean OSM files',
2818
info='If set, OSM files are cleaned from strange characters prior to import (recommended).',
2819
))
2820
2821
self.height_default = attrsman.add(cm.AttrConf('height_default', kwargs.get('height_default', 7.0),
2822
groupnames=['options'],
2823
perm='rw',
2824
name='Default height',
2825
info='Default height of facilities in case no height information is available.',
2826
))
2827
2828
self.type_default = attrsman.add(cm.AttrConf('type_default', kwargs.get('type_default', 'building.yes'),
2829
groupnames=['options'],
2830
perm='rw',
2831
name='Default facility type',
2832
info='Default type of facilities in case no type information is available.',
2833
))
2834
2835
self.add_option('polyfilepath', polyfilepath,
2836
groupnames=['_private'],
2837
cml='--output-file',
2838
perm='r',
2839
name='Poly file',
2840
wildcards='Poly XML files (*.poly.xml)|*.poly.xml',
2841
metatype='filepath',
2842
info='SUMO Poly file in XML format.',
2843
)
2844
2845
self.add_option('typefilepath', typefilepath,
2846
groupnames=['options'],
2847
cml='--type-file',
2848
perm='rw',
2849
name='Type file',
2850
wildcards='Typemap XML files (*.typ.xml)|*.typ.xml',
2851
metatype='filepath',
2852
info="""Typemap XML files. In these file,
2853
OSM building types are mapped to specific facility parameters, is not explicitely set by OSM attributes.""",
2854
)
2855
2856
# --net-file <FILE> Loads SUMO-network FILE as reference to offset and projection
2857
self.add_option('projparams', projparams,
2858
groupnames=['options'],
2859
cml='--proj',
2860
perm='rw',
2861
name='projection',
2862
info='Uses STR as proj.4 definition for projection. Default is the projection of the network, better do not touch!',
2863
is_enabled=lambda self: self.projparams is not None,
2864
)
2865
2866
self.add_option('offset_x', offset_x,
2867
groupnames=['options', 'geometry'],
2868
cml='--offset.x ',
2869
perm='rw',
2870
unit='m',
2871
name='X-Offset',
2872
info='Adds offset to net x-positions; default: 0.0',
2873
is_enabled=lambda self: self.offset_x is not None,
2874
)
2875
self.add_option('offset_y', offset_y,
2876
groupnames=['options', 'geometry'],
2877
cml='--offset.y ',
2878
perm='rw',
2879
unit='m',
2880
name='Y-Offset',
2881
info='Adds offset to net x-positions; default: 0.0',
2882
is_enabled=lambda self: self.offset_y is not None,
2883
)
2884
2885
self.add_option('is_keep_full_type', is_keep_full_type,
2886
groupnames=['options'],
2887
cml='--osm.keep-full-type',
2888
perm='rw',
2889
name='keep full OSM type',
2890
info='The type will be made of the key-value - pair.',
2891
)
2892
2893
self.add_option('is_import_all_attributes', is_keep_full_type,
2894
groupnames=['options'],
2895
cml='--all-attributes',
2896
perm='rw',
2897
name='import all attributes',
2898
info='Imports all OSM attributes.',
2899
)
2900
2901
self.add_option('is_use_name_for_id', is_use_name_for_id,
2902
groupnames=['options'],
2903
cml='--osm.use-name',
2904
perm='rw',
2905
name='use OSM name for id',
2906
info=' The OSM id (not internal ID) will be set from the given OSM name attribute.',
2907
)
2908
2909
self.add_option('polytypefilepath', polytypefilepath,
2910
groupnames=[], # ['_private'],#
2911
cml='--type-file',
2912
perm='rw',
2913
name='Poly type file',
2914
wildcards='Net XML files (*.xml)|*.xml',
2915
metatype='filepath',
2916
info='SUMO Poly type file in XML format.',
2917
is_enabled=lambda self: self.polytypefilepath != '',
2918
)
2919
2920
def update_params(self):
2921
"""
2922
Make all parameters consistent.
2923
example: used by import OSM to calculate/update number of tiles
2924
from process dialog
2925
"""
2926
pass
2927
#self.workdirpath = os.path.dirname(self.netfilepath)
2928
#bn = os.path.basename(self.netfilepath).split('.')
2929
# if len(bn)>0:
2930
# self.rootname = bn[0]
2931
2932
def do(self):
2933
self.update_params()
2934
cml = self.get_cml()
2935
2936
if self.is_clean_osmfile:
2937
for path in self.osmfilepaths.split(','):
2938
path_temp = path+'.clean'
2939
clean_osm(path, path_temp)
2940
#shutil.copy (path_temp, path)
2941
shutil.move(path_temp, path)
2942
2943
# print 'SumonetImporter.do',cml
2944
#import_xml(self, rootname, dirname, is_clean_nodes = True)
2945
self.run_cml(cml)
2946
if self.status == 'success':
2947
if os.path.isfile(self.polyfilepath):
2948
print ' OSM->poly.xml successful, start importing xml files'
2949
self._landuse.import_polyxml(self.rootname, self.rootdirpath,
2950
is_clear=not self.is_merge,
2951
type_default=self.type_default,
2952
height_default=self.height_default)
2953
print ' import poly in sumopy done.'
2954
return True
2955
return False
2956
else:
2957
return False
2958
2959
def get_landuse(self):
2960
# used to het landuse in case landuse has been created
2961
return self._landuse
2962
2963
2964
if __name__ == '__main__':
2965
###############################################################################
2966
# print 'sys.path',sys.path
2967
from agilepy.lib_wx.objpanel import objbrowser
2968
from agilepy.lib_base.logger import Logger
2969
#from coremodules.scenario import scenario
2970
from coremodules.network import network
2971
logger = Logger()
2972
NETPATH = os.path.join(SUMOPYDIR, 'coremodules', 'network', 'testnet')
2973
net = network.Network(logger=logger)
2974
rootname = 'facsp2'
2975
net.import_xml(rootname, NETPATH)
2976
# net.read_sumonodes(os.path.join(NETPATH,'facsp2.nod.xml'))
2977
# net.read_sumoedges(os.path.join(NETPATH,'facsp2.edg.xml'))
2978
landuse = Landuse(net=net, logger=logger)
2979
2980
# landuse.facilities.import_poly(os.path.join(NETPATH,'facsp2.poly.xml'))
2981
landuse.import_xml(rootname, NETPATH)
2982
objbrowser(landuse)
2983
2984