#######################
#-*- coding: utf-8 -*-
'''
library 4 [DM*] @ III OI III by _diag / agruber@tugraz.at
2018/12
2023/12
'''

import rhinoscriptsyntax as rs
import scriptcontext as sc
import Rhino
from System.Drawing import Color
import RhinoPython.Host as __host # 4 _.esc()
import math, random # a + (b - a)*random
import fnmatch # unix filename_match
import struct  # 4 OSM, HGT getCor()
import re      # reg ex
import os, os.path, time
import System, System.DateTime
from datetime import datetime
import sys
from time import gmtime, strftime, ctime
from System.Drawing import Color
from itertools import combinations as cb # 4 convHull



rs.UnitSystem(4)
rs.ViewDisplayMode(rs.CurrentView(), "whireframe")
rs.AddLayer("Default")

import clr
clr.AddReferenceByPartialName('System.Xml')
from System.Xml import XmlTextReader, XmlNodeType, XmlReaderSettings, XmlReader
import System.Xml as xml

#import DM2_lib as dm
global timeX
timeX = time.time()

def setTime():
    global timeX
    timeX = time.time()
setTime()


def getTime(verbose=0, prenull=0):
    global timeX
    tim = float(time.time())
    tim -=  float(timeX)
    if verbose: print str(round(tim,2))+"s"
    #return preNull( round(tim,1), prenull)
    return round(tim,1)
###
### DEM & OSM utilities :
###
#ftp://ftp.glcf.umd.edu/glcf/SRTM/
#https://dds.cr.usgs.gov/srtm/version2_1/SRTM1/Region_06/
#https://dds.cr.usgs.gov/srtm/version2_1/SRTM3/North_America/
### OSMi
#   https://github.com/ll--/Rhino-OSMImport
### bigggg exports
#   https://export.hotosm.org/de/v3/
### osm anaytics
#   http://osm-analytics.org/#/compare/polygon:cpj%7DAimn~Gin%40vErFyWzTJ/2012...now/buildings
# https://gis.stackexchange.com/questions/43743/extracting-elevation-from-hgt-file
#https://search.earthdata.nasa.gov/granules/download.txt?project=8306465326&collection=C1000000240-LPDAAC_ECS


### RMap projection (mercator)

# ----------------------------------------------------------------------------
# OSM routines
# ----------------------------------------------------------------------------
OSMpath = OSMfile = ''
global move2guinea
move2guinea=0




import clr
clr.AddReferenceByPartialName('System.Xml')
from System.Xml import XmlTextReader, XmlNodeType


def rand(a, b=0): # 20171010
    r = random.uniform(a, b) # float
    if (type(a)==int) and  (type(b)==int) : return int(r)
    else: return r



def lat2coord(lat):
    global minLat
    global move2guinea
    val = (6378137.0 * math.log((math.sin(math.radians(lat))+1.0) / math.cos(math.radians(lat)))) - ( move2guinea * (6378137.0 * math.log((math.sin(math.radians(int(minLat)))+1.0) / math.cos(math.radians(int(minLat))))) )
    return val

def lon2coord(lon):
    global minLon
    global move2guinea
    val = (6378137.0 * math.radians(lon)) - ( move2guinea *(6378137.0 * math.radians(int(minLon) )) )
    #abzug = (6378137.0 * math.radians(int(lon)))
    #print val, "-", abzug, "=", val - move2guinea*abzug
    return val


global layer_conditions
def OSM_layer_condi():
    global layer_conditions
    layer_conditions = [
          # added by _diag ________________________>
          {"layer_name": "_parking", 
           "conditions": [ ["amenity", "parking"] ]},
    
          {"layer_name": "_cycling", 
           "conditions": [ ["highway", "cycleway"],
                           ['bridge','*'] ]},
          {'layer_name':'_cycling',  
           'conditions': [ ['highway','cycleway'] ]},
          {'layer_name':'_cycling',  
           'conditions': [ ['highway','cycleway'],  
                           ['embankment','*'] ]},
    
          {"layer_name": "_tststs_bicylce", 
          "conditions": [ ["amenity", "bicycle_parking"] ]},
          # added by _diag ________________________<

      {'layer_name':'buildings',  
       'conditions': [ ['building','*'] ]},
       
      # cycleways
      {'layer_name':'road_cycle_bridge',  
       'conditions': [ ['highway','cycleway'],  
                       ['bridge','*'] ]},
                       
      {'layer_name':'road_cycle_embankment',  
       'conditions': [ ['highway','cycleway'],  
                       ['embankment','*'] ]},
    
      {'layer_name':'road_cycle',  
       'conditions': [ ['highway','cycleway'] ]},
    
      # redestrian roads and paths
      {'layer_name':'pedestrian_steps',  
       'conditions': [ ['highway','steps'] ]},
      {'layer_name':'pedestrian_footway_bridge',  
       'conditions': [ ['highway','footway'],
                       ['bridge','*']  ]},
      {'layer_name':'pedestrian_footway_tunnel',  
       'conditions': [ ['highway','footway'],
                       ['tunnel','*']  ]},
      {'layer_name':'pedestrian_footway_embankment',  
       'conditions': [ ['highway','footway'],
                       ['embankment','*']  ]},
      {'layer_name':'pedestrian_footway',  
       'conditions': [ ['highway','footway'] ]},
      {'layer_name':'pedestrian_path_bridge',  
       'conditions': [ ['highway','path'],
                       ['bridge','*']  ]},
      {'layer_name':'pedestrian_path_tunnel',  
       'conditions': [ ['highway','path'],
                       ['tunnel','*']  ]},
      {'layer_name':'pedestrian_path_embankment',  
       'conditions': [ ['highway','path'],
                       ['embankment','*']  ]},
      {'layer_name':'pedestrian_path', 
       'conditions': [ ['highway','path'] ]},
      # pedestrian streets
      {'layer_name':'pedestrian_area',  
       'conditions': [ ['highway','pedestrian'],
                       ['area', '*'] ]},
      {'layer_name':'pedestrian_bridge',  
       'conditions': [ ['highway','pedestrian'],
                       ['bridge','*']  ]},
      {'layer_name':'pedestrian_tunnel',  
       'conditions': [ ['highway','pedestrian'],
                       ['tunnel','*']  ]},
      {'layer_name':'pedestrian_embankment',  
       'conditions': [ ['highway','pedestrian'],
                       ['embankment','*']  ]},
      {'layer_name':'pedestrian',  
       'conditions': [ ['highwayX','pedestrianX'] ]},
      # roads
      {'layer_name':'road_motorway_bridge',  
       'conditions': [ ['highway','motorway'],
                       ['bridge','*']  ]},
      {'layer_name':'road_motorway_tunnel',  
       'conditions': [ ['highway','motorway'],
                       ['tunnel','*']  ]},
      {'layer_name':'road_motorway_embankment',  
       'conditions': [ ['highway','motorway'],
                       ['embankment','*']  ]},
      {'layer_name':'road_motorway',  
       'conditions': [ ['highway','motorway'] ]},
      {'layer_name':'road_motorway_link',  
       'conditions': [ ['highway','motorway_link'] ]},
       
      {'layer_name':'road_trunk_bridge',  
       'conditions': [ ['highway','trunk'],
                       ['bridge','*']  ]},
      {'layer_name':'road_trunk_tunnel',  
       'conditions': [ ['highway','trunk'],
                       ['tunnel','*']  ]},
      {'layer_name':'road_trunk_embankment',  
       'conditions': [ ['highway','trunk'],
                       ['embankment','*']  ]},
      {'layer_name':'road_trunk',  
       'conditions': [ ['highway','trunk'] ]},
      {'layer_name':'road_trunk_link',  
       'conditions': [ ['highway','trunk_link'] ]},
       
      {'layer_name':'road_primary_bridge',  
       'conditions': [ ['highway','primary'],
                       ['bridge','*']  ]},
      {'layer_name':'road_primary_tunnel',  
       'conditions': [ ['highway','primary'],
                       ['tunnel','*']  ]},
      {'layer_name':'road_primary_embankment',  
       'conditions': [ ['highway','primary'],
                       ['embankment','*']  ]},
      {'layer_name':'road_primary',  
       'conditions': [ ['highway','primary'] ]},
      {'layer_name':'road_primary_link',  
       'conditions': [ ['highway','primary_link'] ]},
       
      {'layer_name':'road_secondary_bridge',  
       'conditions': [ ['highway','secondary'],
                       ['bridge','*']  ]},
      {'layer_name':'road_secondary_tunnel',  
       'conditions': [ ['highway','secondary'],
                       ['tunnel','*']  ]},
      {'layer_name':'road_secondary_embankment',  
       'conditions': [ ['highway','secondary'],
                       ['embankment','*']  ]},
      {'layer_name':'road_secondary',  
       'conditions': [ ['highway','secondary'] ]},
      {'layer_name':'road_secondary_link',  
       'conditions': [ ['highway','secondary_link'] ]},
       
      {'layer_name':'road_secondary_bridge',  
       'conditions': [ ['highway','secondary'],
                       ['bridge','*']  ]},
      {'layer_name':'road_secondary_tunnel',  
       'conditions': [ ['highway','secondary'],
                       ['tunnel','*']  ]},
      {'layer_name':'road_tertiary_embankment',  
       'conditions': [ ['highway','tertiary'],
                       ['embankment','*']  ]},
      {'layer_name':'road_tertiary',  
       'conditions': [ ['highway','tertiary'] ]},
      {'layer_name':'road_tertiary_link',  
       'conditions': [ ['highway','tertiary_link'] ]},
    
      {'layer_name':'road_track_bridge',  
       'conditions': [ ['highway','track'],
                       ['bridge','*']  ]},
      {'layer_name':'road_track_tunnel',  
       'conditions': [ ['highway','track'],
                       ['tunnel','*']  ]},
      {'layer_name':'road_track_embankment',  
       'conditions': [ ['highway','track'],
                       ['embankment','*']  ]},
      {'layer_name':'road_track',  
       'conditions': [ ['highway','track'] ]},
       
      {'layer_name':'road_residential',  
       'conditions': [ ['highway','living_street'] ]},
      {'layer_name':'road_residential',  
       'conditions': [ ['highway','residential'] ]},
       
      {'layer_name':'road_service',  
       'conditions': [ ['highway','service'] ]},
    
      {'layer_name':'road_unknown',  
       'conditions': [ ['highway','road'] ]},
       
      {'layer_name':'road_proposed',  
       'conditions': [ ['highway','proposed'] ]},
      {'layer_name':'road_proposed',  
       'conditions': [ ['highway','construction'] ]},
       
      {'layer_name':'road_other',  
       'conditions': [ ['highway','*'] ]},
        
      # railways
      {'layer_name':'railway_tram_bridge',  
       'conditions': [ ['railway','tram'],
                       ['bridge','*'] ]},
                       
      {'layer_name':'railway_tram_embankment',  
       'conditions': [ ['railway','tram'],
                       ['embankment','*'] ]},
                       
      {'layer_name':'railway_tram_tunnel',  
       'conditions': [ ['railway','tram'],
                       ['tunnel','*'] ]},
                       
      {'layer_name':'railway_tram',  
       'conditions': [ ['railway','tram'] ]},
       
      {'layer_name':'railway_subway',  
       'conditions': [ ['railway','subway'] ]},
      
      
      {'layer_name':'railway_bridge',  
       'conditions': [ ['railway','rail'],
                       ['bridge','*'] ]},
      {'layer_name':'railway_bridge',  
       'conditions': [ ['railway','light_rail'],
                       ['bridge','*'] ]},
                       
      {'layer_name':'railway_embankment',  
       'conditions': [ ['railway','rail'],
                       ['embankment','*'] ]},
      {'layer_name':'railway_embankment',  
       'conditions': [ ['railway','light_rail'],
                       ['embankment','*'] ]},
    
      {'layer_name':'railway_tunnel',  
       'conditions': [ ['railway','rail'],
                       ['tunnel','*'] ]},
      {'layer_name':'railway_tunnel',  
       'conditions': [ ['railway','light_rail'],
                       ['tunnel','*'] ]},
                       
      {'layer_name':'railway',  
       'conditions': [ ['railway','rail'] ]},
      {'layer_name':'railway',  
       'conditions': [ ['railway','light_rail'] ]},
      {'layer_name':'railway other',  
       'conditions': [ ['railway','*'] ]},
      
      # aerialways
      {"layer_name": "aerial tram", 
       "conditions": [ ["aerialway","cable_car"] ]},
      {"layer_name": "aerial tram chair", 
       "conditions": [ ["aerialway","chair_lift"] ]},
      {"layer_name": "aerial tram chair", 
       "conditions": [ ["aerialway","gondola"] ]},
      {"layer_name": "aerial tram chair", 
       "conditions": [ ["aerialway","mixed_lift"] ]},
    
      {"layer_name": "aeroway", 
       "conditions": [ ["aeroway","aerodrome"] ]},
      {"layer_name": "aeroway", 
       "conditions": [ ["aeroway","apron"] ]},
      {"layer_name": "aeroway", 
       "conditions": [ ["aeroway","helipad"] ]},
      {"layer_name": "aeroway", 
       "conditions": [ ["aeroway","runway"] ]},
      {"layer_name": "aeroway", 
       "conditions": [ ["aeroway","taxiway"] ]},
      
      # water
      {"layer_name": "water", 
       "conditions": [ ["natural","water"] ]},
      {"layer_name": "water", 
       "conditions": [ ["natural","spring"] ]},
      {"layer_name": "water", 
       "conditions": [ ["natural","riverbank"] ]},
      {"layer_name": "water", 
       "conditions": [ ["leisure","marina"] ]},
      {"layer_name": "coast", 
       "conditions": [ ["natural","coast"] ]},
      
      # green
      {"layer_name": "park", 
       "conditions": [ ["leisure","park"] ]},
      {"layer_name": "park", 
       "conditions": [ ["leisure","garden"] ]},
      {"layer_name": "park", 
       "conditions": [ ["leisure","village_green"] ]},
      {"layer_name": "forest", 
       "conditions": [ ["landuse","forest"] ]},
      {"layer_name": "forest", 
       "conditions": [ ["natural","wood"] ]},
       
      # playgrounds
      {"layer_name": "sport", 
       "conditions": [ ["leisure","pitch"] ]},
      {"layer_name": "sport", 
       "conditions": [ ["leisure","stadium"] ]},
      
      # boundary
      {"layer_name": "borders", 
       "conditions": [ ["boundary","administrative"] ]},
      
      # contours
      {"layer_name": "contours_major", 
       "conditions": [ ["contour_ext","elevation_major"] ]},
      {"layer_name": "contours_minor", 
       "conditions": [ ["contour","elevation"] ]},
      

      
      # rest
      {'layer_name':'other',  
       'conditions': [ ['*','*'] ]}
    ]
OSM_layer_condi()

# ----------------------------------------------------------------------------
# Parser & Handler
# ----------------------------------------------------------------------------    

class ipyNetParser():
    """ 
    System.Xml.XmlTextReader parser 
    """

    def __init__( self, xml_doc, root ):
        self.__reader = None
        result = None
        try:
            try:
                self.__reader = XmlTextReader( xml_doc )
                result = self.parse( root )
            finally:
                if self.__reader != None:
                    self.__reader.Close()
                del self.__reader
        except ValueError:
            raise ValueError
        return result
    
    def parse(self,element):
        r = self.__reader
        r.Read()
        while r.Read():
            if r.LocalName.Equals( element ):
                while r.Read():
                    if r.NodeType == XmlNodeType.Element:
                        self.startElement( r.LocalName )
                    elif r.NodeType == XmlNodeType.EndElement:
                        self.endElement( r.LocalName )

    def startElement( self, elementName):
        """
        Abstract method: Implemented by sub-classes to match pattern 
        on element's start

        MUST be implemented by handler subclass.
        """

    def endElement( self, elementName):
        """
        Abstract method: Implemented by sub-classes to match pattern 
        on element's end

        MUST be implemented by handler subclass.
        """

    def getAttribute( self, attrName ):
        """
        Returns value of inserted attribute.
        """
        if self.__reader.HasAttributes:
            self.__reader.MoveToAttribute(attrName)
            return self.__reader.Value

class osmNetHandler( ipyNetParser ):
    """
    A handler to deal with nodes in OSM file 

    example:
      m = osmNetHandler("~/maps/data.osm")
      for id, way in m.elements['way'].iteritems():
        #do something
    """
  
    _inside_element = None
    _relation_types = ["multipolygon", "boundary", "route"]


    def __init__( self, xml_doc ):
        self.elements = {
            "node": {},
            "way": {},
            "relation": {} 
        }
    
        for relation_type in self._relation_types:
            self.elements["relation"][relation_type] = {}
    
        self.lat = {}   
        self.lon = {}  
    
        ipyNetParser.__init__( self, xml_doc, "osm" )

    def startElement( self, name ):
        if name in ["node", "way", "relation"]:
            self._inside_element = name
            self._current_id = int(self.getAttribute("id"))
            self._current_attributes = {}
            self._current_children = []
      
            if name == "node":
                self.lat[self._current_id]=float(self.getAttribute("lat"))
                self.lon[self._current_id]=float(self.getAttribute("lon"))
            elif name == "relation":
                self._relation_type = None
                self._outer_members = []
        
        elif self._inside_element:
            if name in ["nd", "member"]:
                # ignore other children [ehm nodes?] of relation than way
                if name == "member" and self.getAttribute("type") != "way":
                    return
                ref = int(self.getAttribute("ref"))
                self._current_children.append(ref)
                if name == "member" and self.getAttribute("role") == "outer":
                    self._outer_members.append(ref)
        
            elif name == "tag":
                attr_key = self.getAttribute("k")
                if attr_key == "created_by":
                    return

                self._current_attributes[attr_key]=self.getAttribute("v")
                if self._inside_element == "relation" and \
                   self.getAttribute("v") in self._relation_types:
                    self._relation_type = self.getAttribute("v")
    
    
    def endElement(self, name):
        id = self._current_id
        if name in ["node", "way", "relation"]:
            self._inside_element = None
      
            # ignore unattributed nodes
            if name == "node" and self._current_attributes:
                return
      
            element = {
                "attributes": self._current_attributes
            }
      
            if name != "node":
                element["children"] = self._current_children
      
            if name == "relation":
                if self._relation_type:
                    if self._relation_type == "multipolygon":
                        element["outer_members"] = self._outer_members
                    self.elements[name][self._relation_type][id] = element
            else:
                self.elements[name][id] = element
      

# ----------------------------------------------------------------------------
# Rhino routines
# ----------------------------------------------------------------------------

def getLayerName(attributes):
    layer_name = 'Default'
    global layer_conditions
    for layer_condition in layer_conditions:
        pass_layer = False
        for condition in layer_condition["conditions"]:
            if condition[0] != "*" and condition[0] not in attributes:
                pass_layer = True
            elif condition[1] != '*' and attributes[condition[0]] != condition[1]:
                pass_layer = True
    
        if not pass_layer:
            return layer_condition["layer_name"]
    return layer_name
    
def getWayPoints(children_id, model):
    """Get way nodes and project coordiates
    """
    lon = model.lon
    lat = model.lat
    result = []
    for x in children_id:
        if x in lon and x in lat:
            result.append([lon2coord(lon[x]), lat2coord(lat[x]), 0])
    return result
    
def createCurve(waypoints, layer=None):
    #print waypoints
    if not waypoints or len(waypoints) < 2:
        return
    elif len(waypoints) <= 3 and waypoints[0][:] == waypoints[-1][:]:
        return
    curve = rs.AddPolyline(waypoints)
    
    if curve and layer:
        rs.ObjectLayer(curve, layer)
        #curve = rs.ScaleObject(curve, [0,0,0], [2,2,2], copy=False)
        #print "CU____"
    return curve

def _ag_layerColors():
    layerNames = rs.LayerNames()
    if layerNames:
        for name in layerNames:
            #rs.LayerColor(name, randomcolor())
            if name.count("OSM"): rs.LayerVisible(name, 1)
            if name == "OSM::buildings": rs.LayerColor(name,(255,0,0))
            if name == "OSM::buildings::_bldg": rs.LayerColor(name,(255,0,0))
            if name == "OSM::buildings::_bldg3D_crv": rs.LayerColor(name,(255,0,0))
            if name == "OSM::buildings::_bldg3D_srf": rs.LayerColor(name,(100,150,200))

            if name == "OSM::forest"   : rs.LayerColor(name,(0,160,0))
            if name == "OSM::park"     : rs.LayerColor(name,(200,240,40))
            if name == "OSM::water"    : rs.LayerColor(name,(40,140,240))
            if name == "OSM::sport"    : rs.LayerColor(name,(240,80,40))
            if name == "OSM::other"    : rs.LayerColor(name,(200,200,200))
            if name == "OSM::_parking"   : rs.LayerColor(name,(240,140,0))
            if name == "OSM::_railway"   : rs.LayerColor(name,(240,240,40))
            if name == "OSM::_road"      : rs.LayerColor(name,(240,240,240))
            if name == "OSM::_cycling"   : rs.LayerColor(name,(240,140,120))

def _ag_diverseLayers():
    rs.AddLayer("OSM::diverses", [200,0,200])
    for lay in rs.LayerNames():
        #print lay
        if "OSM::" in lay and not "buildings" in lay and not "diverses" in lay:
            #print lay
            rs.AddLayer(lay[5:],color=rs.LayerColor(lay),parent="OSM::diverses")
            rs.ObjectLayer( rs.ObjectsByLayer(lay), "OSM::diverses::"+lay[5:])
            rs.DeleteLayer(lay)
    if 1:
        for lay in rs.LayerNames():
            if 1 and lay[5:] in ["water", "forest", "park", "sport", "other"]:
                rs.DeleteLayer(lay[5:])
    rs.ExpandLayer( "OSM", 1 )
    rs.ExpandLayer( "OSM::diverses", 0 )


OSMpath = "C:/"

def OSMi(OSMfile=OSMfile, OSMpath= OSMpath, do3D=1, minHeight=3.0, heightFac = 1.0, trimBounds=0, randomize=[10, 10], doAllCurves=1, move2Guinea=0, verbose=0):
    rs.EnableRedraw(0)
    global move2guinea
    move2guinea=move2Guinea
    OSM_overpass_testAndChange(OSMpath, OSMfile, 1)
    OSM_getBounds(OSMfile, OSMpath, drawRect=0, trim=0, verbose=0)
    rs.ZoomExtents()
    rs.Redraw()
    #    global minLat
    #    global minLon
    #    global maxHeight
    maxHeight = 0.0
    diplayMode = rs.ViewDisplayMode(rs.CurrentView())
    diplayMode = rs.ViewDisplayMode(rs.CurrentView(), "wireframe")
    
    ### << need 4 guinea_move
    # ------------------------------------------------------------------------
    # additional layers
    rs.AddLayer("OSM")
    rs.AddLayer("buildings",  [255, 0, 0]    , parent="OSM" )
    rs.AddLayer("_bldg",   [255, 0, 0]    , parent="OSM::buildings" )
    rs.AddLayer("_bldg3D_crv",   [255, 0, 0]    , parent="OSM::buildings" )
    rs.AddLayer("_bldg3D_srf",   [50, 150, 200] , parent="OSM::buildings" )
    rs.AddLayer("_road",      [250, 240, 220], parent="OSM")
    rs.AddLayer("_railway",   [240, 240, 240], parent="OSM")
    rs.AddLayer("_pedestrian",[140, 240, 240], parent="OSM")
    # default layer:
    initial_layers = rs.LayerNames()
    global layer_conditions
    for layer_condition in layer_conditions:
        rs.AddLayer(layer_condition["layer_name"], parent="OSM" )
    # ------------------------------------------------------------------------ 
    # initial Rhino settings
    ###diag:
    setTime()
    existingObjects = []
    for obj in rs.AllObjects(): existingObjects.append(rs.ObjectName(obj))
    vorher = len(rs.AllObjects())
    rs.EnableRedraw(0)
    ###diag
    # ------------------------------------------------------------------------ 
    # get datamodel
    if not OSMfile:
        OSMfile = rs.OpenFileName()
    if not OSMfile:
        return
    osm_model = osmNetHandler(OSMpath+OSMfile)
    model_elements = osm_model.elements
    used_ways = []
    for id, relation in model_elements['relation']['multipolygon'].iteritems():
        curves = []
        # obtain layer_name
        layer_name = getLayerName(relation["attributes"]) 
        if layer_name in ["Default", "other"]:
            for outer_member in relation["outer_members"]:
                if outer_member not in model_elements['way']:
                  continue
                layer_name = getLayerName(
                    model_elements['way'][outer_member]["attributes"])
                if layer_name not in ["Default", "other"]:
                    break
        # for child in children
        for way_id in relation["children"]:
            used_ways.append(way_id)
            if way_id not in model_elements["way"]:
                continue
            waypoints = getWayPoints(
                model_elements["way"][way_id]["children"], osm_model)
            curve = ""
            if doAllCurves and not existingObjects.count("Wcrv_"+str(way_id)):
                curve = createCurve(waypoints, layer_name)
            if curve:
                rs.ObjectName(curve, "Wcrv_"+str(way_id))
                existingObjects.append("Wcrv_"+str(way_id))
    # ------------------------------------------------------------------------
    # create ways ... ONLY WAYS !
    rs.UnselectAllObjects()
    esc()
    for id, way in model_elements['way'].iteritems():
        esc()
        if id in used_ways:
            continue
        waypoints = getWayPoints(way["children"], osm_model)
        ### diag:
        if not existingObjects.count("crv_"+str(id)) and not existingObjects.count("_bldg3D_crv_"+str(id)):
            if doAllCurves:
                #print len(waypoints)
                curve = createCurve(waypoints, getLayerName(way["attributes"])) # layer names match 
                rs.ObjectName(curve, "crv_"+str(id))
            line = model_elements['way'][id]["attributes"]
            if 0 and verbose: print "\nID",id,":",

            height=0.0
            floorHeight=4.0 ### 20230623 as of frankfurt/main
            colCode = 0
            higMeth = "nul"
            if 1 or do3D:
                for item in line:
                    if 0 and verbose: print "line_item:", item, line[item],
                    if 1 and item == "building":
                        height = rand(randomize[0], randomize[1])
                        colCode = 1
                        higMeth = "ran"
                    hightF = hightH = 1.0

                        
                    if 1 and item == "building:levels":
                        floors = line[item]
                        floors = floors.replace(";", "")
                        floors = floors.replace("c", "")
                        floors = floors.replace(" c", "")
                        floors = floors.replace("C", "")
                        floors = floors.replace(" ", "")
                        floors = floors.replace("EG", "")
                        if floors.count("-"):
                            floors = 0.0
                        if floors:
                            #print floors, id
                            if len(floors)>3: floors=0.0
                            floors = float(floors)
                            #if floors.isnumeric():
                            #    floors = float(floors)
                        else:
                            floors = 0.0
                        if id==261256941 or id==137989966: # UN building
                            floorHeight=4.0
                        height = floors*floorHeight
                        hightF = float(height)
                        #if 1 or  id==261256941: print "F", hightF
                        colCode = 2
                        higMeth = "flo"
                        #print "flo", hightF 
                        
                    if 1 and item == "height":
                        height = line[item]
                        height = height.replace(" m", "")
                        height = height.replace("m", "")
                        height = height.replace(" c", "")
                        height = height.replace("c", "")
                        height = height.replace("<", "")
                        height = height.replace(";", ".")
                        if height.count("c") or height.count("f") :
                            floors=0.0 #print "\n_________________CM found_________________"
                        colCode = 3
                        higMeth = "hig"
                        if height == "14.8. 16.8. 14.8. 16.8. 15.2": height = 18.1 ### NYC
                        #print id, "hig =", height, type(height)
                        hightH = float(height)

                        #if height.isnumeric():height = float(height)
                        #else:height = 0.0
                        #print "hightH", hightH
                    if float(height) > maxHeight: maxHeight = float(height)*heightFac
            if float(height) > minHeight and (waypoints[0] == waypoints[-1]) or id==241833352: ## cooper union # check if closed: if not > no good boolin'
                if verbose: print "____3D_srf_height", height
                if not doAllCurves:
                    curve = createCurve(waypoints, getLayerName(way["attributes"]))
                rs.ObjectName(curve, "_bldg3D_crv_"+str(id))
                rs.ObjectLayer(curve, "OSM::buildings::_bldg3D_crv")
                if len(rs.ObjectsByLayer("OSM::buildings::_bldg3D_crv")) % 100==0: rs.Redraw()
                height = float(height)
                if do3D:
                    if id==34633854:  height = 1.0 # empire state base
                    if id==260904072: height = 1.0 # chrysler base
                    if id==713565776: height = 1.0 # WTC base
                    if height < 0.1: height=0.1
                    bldg=rs.ExtrudeCurveStraight(curve, [0,0,0], [0,0, height*heightFac]) 
                    rs.CapPlanarHoles(bldg)
                    name = "_bldg3D_higBy_"+higMeth+"_"+str(id)
                    rs.ObjectName(bldg, name)
                    rs.ObjectLayer(bldg, "OSM::buildings::_bldg3D_srf")
                    rs.ObjectColor(bldg, [250-80*colCode, 100, 80*colCode])
    if len( rs.AllObjects() ) % 300 == 0:
        rs.ZoomExtents()
        rs.Redraw()

    ###diag:
    #fil = OSMpath+OSMfile
    #os.stat(fil)
    #os.stat_result(st_mode=33188, st_ino=6419862, st_dev=16777220, st_nlink=1, st_uid=501, st_gid=20, st_size=1564, st_atime=1584299303, st_mtime=1584299400, st_ctime=1584299400)
    #print "siz", os.stat(OSMpath+OSMfile).st_size/1024.0

    print "|||||||||||||||"
    print "||| OSMi_mport:", OSMfile,
    print "||| siz =",round( os.stat(OSMpath+OSMfile).st_size/1024.0/1000,2), "MB"
    _ag_layerColors()
    rs.ObjectLayer( rs.ObjectsByLayer("OSM::buildings"), "OSM::buildings::_bldg") # 20180516 @ diag
    rs.ObjectsByLayer("OSM::buildings::_bldg", 1)
    rs.ObjectsByLayer("OSM::buildings::_bldg3D_srf", 1)
    rs.ZoomSelected()
    rs.UnselectAllObjects()
    rs.AddLayer( "Default" )
    rs.CurrentLayer( "Default" )
    rs.DeleteLayer( "OSM" ) # 20180516 @ diag
    #if trimBounds:
    OSM_getBounds(OSMfile, OSMpath=OSMpath, drawRect=1, trim=trimBounds, verbose=1)
    OSM_makeSrf( ["water", "forest", "park", "sport", "other"] ) # 20180516 @ diag
    nacher = len(rs.AllObjects())

    print "||||||||||||||| total objects vorher/nachher:", str(vorher)+"/"+str(nacher), "new =", (nacher - vorher),
    print " ||| done in", str(getTime())+"s\n"
    echo(1)

    ###############################################
    ######### 2022 05 18 ### remove innenhoefe u.a.
    if do3D and "OSM::buildings::_bldg" in rs.LayerNames():
        rs.AddLayer( "OSM::buildings::_bldg3D_srf", [200,0,0])
        rs.DeleteObjects(rs.ObjectsByName("*inner*"))
        allCrvs = rs.ObjectsByLayer("OSM::buildings::_bldg")
        for i,crv in enumerate(allCrvs[0:]):
            if not rs.IsCurveClosed(crv):
                allCrvs.remove(crv)
        rs.EnableRedraw(0)
        rs.UnselectAllObjects()
        innerCrvs = []
        for i,crv in enumerate(allCrvs[0:]):
            esc()
            if i%32==0: rs.Redraw()
            if crv not in innerCrvs:
                outerNam = rs.ObjectName( crv ).split("_")[1]
                rs.UnselectAllObjects()
                rs.Command("-_SelBoundary SelectionMode=Window  Precise=Yes selID "+str(crv)+" enter enter", 0)
                if rs.SelectedObjects():
                    for item in rs.SelectedObjects():
                        if rs.ObjectType( item ) == 4 and item in allCrvs:
                            innerCrvs.append( item )
                            allCrvs.remove(item)
                        else:
                            rs.UnselectObject(item)
                    if rs.SelectedObjects():
                        rs.SelectObject(crv)
                        height = rand(randomize[0], randomize[1])
                        rs.Command("_ExtrudeCrv Output=Surface BothSides=No DeleteInput=No Solid=Yes "+str(height)+" enter", 0)
                        #obj=rs.ExtrudeCurveStraight(crv, [0,0,0], [0,0, height])
                        obj = rs.AllObjects()[0]
                        cenZ = rs.SurfaceAreaCentroid(obj)[0][2]
                        if cenZ<0:
                            rs.MoveObject(obj, [0,0,height])
                        rs.ObjectColor(obj, [200,180,100])
                        rs.ObjectName(obj, "_bldg3D_higBy_ran_inner_"+outerNam)
                        rs.ObjectLayer(obj, "OSM::buildings::_bldg3D_srf")
    rs.UnselectAllObjects()
    rs.ViewDisplayMode(rs.CurrentView(), diplayMode)
    ### behuebschung der layerstructure / 2022 11 30 / _diag
    eDup(0)
    _ag_diverseLayers()
    
    if "_bldg3D_srf" in rs.LayerNames():
        color = [195,200,200]
        rgbMat2Lay( "OSM::buildings::_bldg3D_srf", *color, T=0.1, Gloss=0.5, Refl=0.1, matNam="_layMat" )



def OSM_getBounds(OSMfile=OSMfile, OSMpath=OSMpath, drawRect=1, trim=0, verbose=0):
    global minLat
    global minLon
    echo(0)
    settings = XmlReaderSettings()
    settings.IgnoreWhitespace = True
    reader = XmlReader.Create(OSMpath+OSMfile, settings)
    reader.ReadToFollowing("bounds")
    minLon = float(reader.GetAttribute("minlon"))
    #minLon -=  * minLon
    minLat = float(reader.GetAttribute("minlat"))
    #minLat -= move2guinea * minLon
    maxLon = float(reader.GetAttribute("maxlon"))
    maxLat = float(reader.GetAttribute("maxlat"))
    if 1 or verbose:
        print "||| OSM_getBnd:", # OSMfile[0:-4+444],
        print "minLon/minLat "+str(round(minLon,4))+"°/"+str(round(minLat,4))+"°",
    minlon= lon2coord(minLon)
    maxlon= lon2coord(maxLon)
    minlat= lat2coord(minLat)
    maxlat= lat2coord(maxLat)
    pMin = [minlon, minlat, 0]
    dist = rs.VectorLength( pMin )
    if verbose:
        print " >> ", str(round(minlon*0.001,2))+"/"+str(round(minlat*0.001,2)), "KM"
        print "|||           :                 move2guinea =",move2guinea," >>  ca",round((dist*0.001), 2) , "kilometers from guinea (== origin | East/North => x/y !)"
    if 1 or drawRect:
        rs.AddLayer("OSM")
        rs.Command("cplane W T enter", 0)
        rs.ZoomExtents()
        rs.Redraw()
        rec = rs.AddRectangle([minlon, minlat,0], (maxlon-minlon), (maxlat-minlat))
        rs.ZoomBoundingBox(rs.BoundingBox(rec))
        rs.Redraw()
        rs.ObjectName( rec, "bounds_"+OSMfile[0:-4])
        rs.ObjectColor(rec, [0,191,191])
        rs.ObjectLayer(rec, "OSM")
        if drawRect and trim:
            rs.UnselectAllObjects()
            for obj in rs.AllObjects():
                if rs.ObjectType(obj) != 4 or rs.IsCurveClosed(obj):
                    rs.HideObject( obj )
            rs.UnselectAllObjects()
            rs.AllObjects(select=1)
            rs.ShowObject( rec )
            if rs.SelectedObjects():
                rs.Command("_-split selid "+str(rec)+" enter", 0)
            offsetted = rs.OffsetCurve( rec, [-1,-1, 0], 0.1 )[0]
            rs.ObjectColor( offsetted, [200,0,0])
            rs.UnselectAllObjects()
            rs.Command("_selBoundary SelectionMode=W Precise=Yes selid "+str(offsetted)+" -enter invert enter delete enter", 0)# -enter invert 
            rs.ShowObjects(rs.AllObjects())
    return [[minlon, minlat,0],[maxlon, minlat,0], [maxlon, maxlat,0], [minlon, maxlat,0]]# , [minlon, minlat,0]]


def OSM_way_checker(OSMfile, OSMpath=OSMpath, checkKey="value", checkVal="value", verbose=0):
    print "\n||| diag's OSM_way_checker checking @", OSMfile,
    settings = XmlReaderSettings()
    settings.IgnoreWhitespace = True
    OSMfile = OSMpath+OSMfile
    reader = XmlReader.Create(OSMfile, settings)
    crvs=[]
    cnt = 0
    if not rs.IsLayer("OSM"):
        print "||| no layer \"OSM\" found >> no function |||\n"
    else: print
    ### WAYs
    while rs.IsLayer("OSM") and reader.ReadToFollowing("osm"):
        reader.ReadStartElement()
        while reader.ReadToNextSibling("way") and cnt < 1000001:
            id = reader.GetAttribute("id")
            while reader.ReadToDescendant("tag"):
                i=0
                #print id, "tag_k"+str(i), reader.GetAttribute("k")
                if reader.GetAttribute("k").count(checkKey) or reader.GetAttribute("v").count(checkVal):
                    #print id, "_____", reader.GetAttribute("k"), reader.GetAttribute("v")
                    cnt += 1
                    if rs.IsLayer("OSM") and rs.ObjectsByName("*_"+id)[0] and rs.ObjectType(rs.ObjectsByName("*_"+id)[0])==4:
                        crvs.append(rs.ObjectsByName("*_"+id)[0])
                        #print "len crvs", len(crvs)
                    if verbose: print id, cnt, "k", reader.GetAttribute("k"), " v:", reader.GetAttribute("v")#, "=> crv_"+id
                    if verbose==3: print id, "__", len(rs.ObjectsByName("crv_"+id))
                while 1 and reader.ReadToNextSibling("tag"):
                    i += 1
                    #print "__tag_k"+str(i), reader.GetAttribute("k")
                    # ori: if reader.GetAttribute("k").count(checkKey) and reader.GetAttribute("v").count("*"+checkVal+"*"):
                    if reader.GetAttribute("k").count(checkKey) or reader.GetAttribute("v").count(checkVal):# checkVal in reader.GetAttribute("v"):#.count("*"+checkVal+"*"):
                        #print "XXX_: k.count", reader.GetAttribute("k").count(checkKey), "-- v:", reader.GetAttribute("v"),
                        #print "XXX\t_: k", reader.GetAttribute("k"), "\t\t\t_: v", reader.GetAttribute("v")
                        cnt += 1
                        if rs.IsLayer("OSM") and rs.ObjectsByName("*_"+id)[0] and rs.ObjectType(rs.ObjectsByName("*_"+id)[0]) == 4 :
                            crvs.append(rs.ObjectsByName("*_"+id)[0])
                        if verbose==2: OSM_findID( id, verbose=1 )
                        if verbose: print id, cnt, "k", reader.GetAttribute("k"), " v:", reader.GetAttribute("v")#, "=> crv_"+id
                        if verbose==3: print id, "__", len(rs.ObjectsByName("crv_"+id))
                while reader.IsStartElement():reader.Skip()
    if rs.IsLayer("OSM"): print "||| diag's OSM_way_checker  found",cnt,"WAYs  with key \""+checkKey+"\" OR val \""+checkVal+"\""
    reader.Close()
    return crvs
### USAge
#print OSM_nodeChecker("graz_grieskai_0.osm", OSMpath = OSMpath,  attributeX="bicycle_parking")[0]


def OSM_node_checker(OSMfile, OSMpath = "L:\\OSM\\", checkKey="diag", checkVal="diag", andOr = "or", verbose=1):
    #print "::: OSM_node_checker:"#, OSMfile[0:-4], "- check k=\""+str(checkKey)+"\" |", andOr.upper(),"| v=\""+checkVal+"\""
    print "\n||| diag's OSM_node_checker checking @", OSMfile,
    global minLon
    global minLat
    
    settings = XmlReaderSettings()
    settings.IgnoreWhitespace = True
    OSMfile = OSMpath+OSMfile
    
    reader = XmlReader.Create(OSMfile, settings)
    IDs = []
    coords = []
    cnt = 0
    if not rs.IsLayer("OSM"):
        print "||| no layer \"OSM\" found >> no function |||\n"
    else:
        print
        OSM_getBounds(OSMfile, OSMpath, drawRect=0, trim=0, verbose=0)
    ### NODE
    while rs.IsLayer("OSM") and reader.ReadToFollowing("osm"):
            reader.ReadStartElement()
            while reader.ReadToNextSibling("node"): ### was node
                lon= lon2coord(float(reader.GetAttribute("lon")))
                lat= lat2coord(float(reader.GetAttribute("lat")))
                id = reader.GetAttribute("id")
                if reader.ReadToDescendant("tag") and reader.GetAttribute("k") != "created_by":
                    if reader.GetAttribute("k").count(checkKey) or reader.GetAttribute("v").count(checkVal):
                        if verbose: print id,"\t", cnt,"k="+reader.GetAttribute("k"), " v="+reader.GetAttribute("v")
                        cnt += 1
                        coords.append([lon,lat,0])
                    while reader.ReadToNextSibling("tag"):
                        if reader.GetAttribute("k").count(checkKey) or reader.GetAttribute("v").count(checkVal):
                            if verbose: print id,"\t", cnt,"k="+reader.GetAttribute("k"), " v="+reader.GetAttribute("v")
                            cnt += 1
                            coords.append([lon,lat,0])
                            IDs.append( id )
                            #print "ID - ", id
                    while reader.IsStartElement():reader.Skip()
    reader.Close()
    if len(coords):
        coords = rs.CullDuplicatePoints(coords, tolerance=0.00000001)
    #print cnt, "findings >",len(coords),"coords\n'''''''''''''''''''''''''''''"
    if rs.IsLayer("OSM"): print "||| diag's OSM_node_checker found", cnt, "NODEs ("+str(len(coords))+" coords) with k=\""+checkKey+"\" "+str.upper(andOr)+" val=\""+checkVal+"\""
    return coords

### USAge
#if 1: coords = OSM_node_checker("cooper_square", OSMpath = "L:\\OSM\\", checkKey="diag", checkVal="diag", andOr = "or", verbose=1)
#   print len(coords)


def OSM_ID_checker(OSMfile, OSMpath=OSMpath, checkID="value", verbose=0):
    print "||| diag's ID_checker checking @", OSMfile, ">>> ID", checkID
    settings = XmlReaderSettings()
    settings.IgnoreWhitespace = True
    OSMfile = OSMpath+OSMfile
    reader = XmlReader.Create(OSMfile, settings)
    crvs=[]
    cnt = 0
    while reader.ReadToFollowing("osm"):
        reader.ReadStartElement()
        while reader.ReadToNextSibling("way") and cnt < 1000001:
            id = reader.GetAttribute("id")
            #id = reader.GetAttribute("ref")
            if id == checkID:
                while reader.ReadToDescendant("tag"):
                    i=-1
                    key = reader.GetAttribute("k")
                    val = reader.GetAttribute("v")
                    cnt += 1
                    if rs.ObjectsByName("*_"+id):crvs.extend(rs.ObjectsByName("*_"+id))
                    print cnt, "\tk:", key, "\t v:", val#, "=> crv_"+id
                    while 1 and reader.ReadToNextSibling("tag"):
                        key = reader.GetAttribute("k")
                        val = reader.GetAttribute("v")
                        cnt += 1
                        if rs.ObjectsByName("*_"+id):crvs.extend(rs.ObjectsByName("*_"+id))
                        print cnt, "\ttk:", key, "\t v:", val#, "=> crv_"+id
    if not cnt:
        print "*** ID ", checkID, ": \tno findings @ ", OSMfile
    reader.Close()
    rs.SelectObjects(crvs)
    rs.ZoomSelected()

### USAge
# OSM_ID_checker(OSMfile, OSMpath=OSMpath, checkID="464974861", verbose=0)


def OSM_markCoords(coords, hig=50, rad=10, lay="_node_marks", color=[0,255,255], doDelete=1):
    rs.EnableRedraw(0)
    rs.AddLayer(lay, [200,0,200], parent="OSM")
    rs.CurrentLayer(lay)
    if doDelete: rs.DeleteObjects(rs.ObjectsByLayer(lay))
    rs.Redraw()
    for i, cor in enumerate(coords):
        #srfs=rs.ExplodePolysurfaces( rs.AddCone([cor[0],cor[1],0], hig, rad), 1)
        #rs.ObjectColor(srfs[1], color)#
        srfs= rs.AddCone([cor[0],cor[1],0], hig, rad)
        rgbMat2Obj(srfs, color[0], color[1], color[2])
    rs.Redraw()


def OSM_makeSrf( layerz ):
    for lay in layerz:
        for nam in rs.LayerNames():
            if "OSM::diverses::"+lay == nam:
                newNam = nam+"::_"+lay+"_srf"
                #print "found :", nam,">>", newNam
                rs.AddLayer("_"+lay+"_srf", rs.LayerColor(nam),  parent=nam)
                rs.DeleteObjects( rs.ObjectsByLayer(newNam) )
                rs.CurrentLayer( newNam )
                crvs = rs.ObjectsByLayer(nam)
                for crv in crvs:
                    if rs.IsCurveClosed( crv ):
                        id  = rs.ObjectName(crv)[4:]
                        srf = rs.AddPlanarSrf( crv )
                        rgbMat2Obj( srf, rs.ColorRedValue(rs.LayerColor(nam)), rs.ColorGreenValue(rs.LayerColor(nam)), rs.ColorBlueValue(rs.LayerColor(nam)) )
                        #print id, srf
                        if srf:
                            rs.ObjectName( srf, "_"+lay+"_"+str(id) )
                            rs.FlipSurface( srf, True)
                            if lay == "other": rs.MoveObject(srf, [0,0,-0.1])
                            if lay == "park":  rs.MoveObject(srf, [0,0,0.1])
                        if 0 and not rs.IsCurveClosed(crv):
                            print "nixi",nam, id
                            rs.SelectObject( crv )
    rs.CurrentLayer( "Default" )
### USAge
#OSM_makeSrf( ["water", "forest", "park", "sport", "other"] )


# newLay must be like "_railway", "_road"
def makeOSM_additionalLayer (newLay, color=[250,200,0], height=10):
    rs.AddLayer(newLay, color, parent="OSM")
    rs.AddLayer(newLay+"_srf", rs.LayerColor("OSM::"+newLay), parent="OSM::"+newLay)
    crvs = []
    for name in rs.LayerNames():
         if name.count(newLay[1:]) and name != ("OSM::"+newLay):
            #print "\tname___",name,"count ", name.count("OSM::"+namY)
            crvs.extend(rs.ObjectsByLayer(name))
    for crv in crvs:
        if rs.ObjectType(crv) == 4:
            srf=rs.ExtrudeCurveStraight(crv, [0,0,0], [0,0, height])
            rs.ObjectLayer(srf, "OSM::"+newLay+"::"+newLay+"_srf")

### USAge
#makeOSM_additionalLayer("_railway", [200,200,0], height=5)
#makeOSM_additionalLayer("_road", [100,200,0], height=5)
#makeOSM_additionalLayer("_road_resi", [250,200,0], height=25)
#makeOSM_additionalLayer("_road_seco", [250,200,0], height=25)



def OSM_makeAllClosedCrvs():
    rs.DeleteObjects(rs.ObjectsByLayer("Default"))
    for crv in rs.ObjectsByType( 4 ):
        if rs.IsCurveClosed(crv) and not rs.ObjectLayer(crv).count("buildings") and rs.ObjectLayer(crv) != "OSM":
            srf = rs.AddPlanarSrf( crv )
            rs.MoveObject( srf, [0,0, -0.2])
            rs.ObjectColor( srf, [0,100,100])
            #rgbMat2Obj( srf, 0, 100, 100 )
            rs.ObjectName( srf, "_all_"+str(rs.ObjectName(crv)[4:]) )



def OSM_findID( findID, verbose=1):
    print "+++ crv_"+findID, "=", len(rs.ObjectsByName("crv_"+findID)),"finded"
    #rs.UnselectAllObjects()
    rs.SelectObject(rs.ObjectsByName("crv_"+findID))
    rs.ZoomSelected()
###USAge
#  OSM_findID( "107403224" )
def OSM_split_bounds():
    splitter = rs.ObjectsByName("bounds_*")[0]
    nam = rs.ObjectName(splitter)
    for crv in rs.ObjectsByType(4):
        if rs.IsCurveClosed(crv)==False:
            rs.Command("split  -SelId {} enter -SelId {} enter".format( crv, splitter))
    splitterX = rs.OffsetCurve(splitter, [0,0,0], -0.0001)[0]
    splitter  = rs.OffsetCurve(splitter, [0,0,0], -0.00015)[0]
    rs.ObjectName(splitter, nam)
    rs.ObjectColor(splitter, [127,255,191])
    rs.Command("selBoundary SelectionMode=C selid "+str(splitterX)+" invert delete",False)
    

def OSM_combinat(baseCurves=[], parent=0):
    lay = "_combinat"
    if parent: newEmptyLayer (lay, [150, 110, 50], parent="OSM::buildings")
    else: newEmptyLayer (lay, [150, 110, 50])
    if not baseCurves:
        baseCurves = rs.ObjectsByLayer("OSM::buildings")
        baseCurves.extend (rs.ObjectsByLayer("OSM::buildings::_bldg3D_crv"))
    srfs = rs.AddPlanarSrf( baseCurves )
    srfs = rs.BooleanUnion(srfs, delete_input=True)
    #rs.SelectObjects(srfs)
    if 1 or srfs:
        for srf in srfs:
            borders = rs.DuplicateSurfaceBorder(srf, type=1)
            #char_curves.append(max(borders, key = rs.CurveArea))
            rs.LockObjects(max(borders, key = rs.CurveArea))
        rs.DeleteObjects(rs.ObjectsByLayer(lay))
        rs.UnlockObjects(rs.AllObjects())
    rs.CurrentLayer("Default")
    if parent: return rs.ObjectsByLayer("OSM::buildings::"+lay)
    else: return rs.ObjectsByLayer(lay)
    



def OSM_bool(): 
    ## try 20200114
    rs.UnitAbsoluteTolerance( 0.00001 )
    currView = rs.CurrentView("Top",  return_name=True)
    currMode = rs.ViewDisplayMode(view=None, mode="Wireframe", return_name=True)
    #rs.ZoomExtents()
    newEmptyLayer("booled", [100,0,200])
    for lay in rs.LayerNames():
        if "buildings" in lay or "booled" in lay or lay=="OSM":
            pass
        else:
            rs.LayerVisible( lay, 0 )
    rs.LayerVisible( "OSM::buildings::_bldg", 0 )
    rs.LayerVisible( "OSM::buildings::_bldg3D_srf", 0 )
    if 1:
        rs.UnselectAllObjects()
        for bor in rs.ObjectsByName("_bldg3D_crv_*")[0:]: #rs.ObjectsByName("bor")[0:]:
            rs.Command("selboundary SelectionMode=Window -selID "+str(bor)+" enter ", 0)
        rs.DeleteObjects( rs.SelectedObjects() )
        crvs = rs.ObjectsByName("_bldg3D_crv_*")
        for crv in crvs:
            rs.ObjectName( rs.AddPlanarSrf( crv ), "planSrf")
    if 1:
        rs.SelectObjects( rs.ObjectsByName("planSrf") )
        rs.Command(" join enter", 0 )
        rs.UnselectAllObjects()
        for srf in rs.ObjectsByName("planSrf"):
            rs.ObjectName(rs.DuplicateSurfaceBorder(srf), "bor" )
        rs.DeleteObjects( rs.ObjectsByName("planSrf") )

    if 1:
        rs.LayerVisible( "OSM::buildings::_bldg3D_srf", 1 )
        rs.LayerVisible( "OSM::buildings::_bldg3D_crv", 0 )
        rs.UnselectAllObjects()
        for bor in rs.ObjectsByName("bor")[0:]:
            off = rs.OffsetCurve( bor, [0,0,0], 0.01 )[0]
            #offi = rs.OffsetCurve( bor, [0,0,0], -0.1 )[0]
            rs.ObjectName(off, "off" )
            #rs.ObjectName(offa, "bor" )
            rs.UnselectAllObjects()
            rs.Command("selboundary SelectionMode=Crossing Precise=Yes -selID "+str(off)+" enter ", 0)
            rs.Command("selboundary SelectionMode=Crossing Precise=Yes -selID "+str(bor)+" enter ", 0)
            rs.UnselectObjects( rs.ObjectsByType( 4 ) )
            rs.Redraw()
            if len(rs.SelectedObjects()) > 1:
                for sel in rs.SelectedObjects():
                    rs.MoveObject( sel, [0,0,random.uniform(-1.0, 1.0)] )
                rs.BooleanUnion( rs.SelectedObjects(), delete_input=0)
            if len(rs.SelectedObjects()) == 1:
                rs.ObjectLayer(rs.CopyObject(rs.SelectedObjects()[0]), "booled")
                
    rs.DeleteObjects( rs.ObjectsByName("bor") )
    rs.DeleteObjects( rs.ObjectsByName("off") )
    rs.CurrentLayer( "Default" )
    currView = rs.CurrentView(currView,  return_name=True)
    currMode = rs.ViewDisplayMode(view=None, mode=currMode, return_name=True)
    rs.UnitAbsoluteTolerance( 0.00001 )
#USAge
#OSM_bool()


def OSM_bool(objects2bool=[], dele=0):
    lay = "_booled"
    rs.AddLayer (lay, [100, 0, 250])
    rs.CurrentLayer( lay )
    #rgbMat2Lay( lay, 100, 0, 250, 0.3, 0, 0.5, "_booled" )
    _2bool = []
    rs.UnitAbsoluteTolerance( 0.1 )
    if not objects2bool:
        objects2bool=rs.ObjectsByLayer("OSM::buildings::_bldg3D_srf")
    print "objects2bool =", len(objects2bool)
    rnd = 0.2
    for obj in objects2bool:
        if rs.ObjectType(obj)==16:# or (rs.ObjectType(obj)==8 and rs.IsSurfaceClosed(obj, 0) and rs.IsSurfaceClosed(obj, 1)):# 
            _2bool.append(obj)
            #rs.SelectObject(obj)
            rs.MoveObject( obj, [rand(-rnd, rnd),rand(-rnd, rnd), rand(rnd)] )
    print "_2bool", len( _2bool )
    #random.shuffle( _2bool )
    #booled = rs.BooleanUnion(_2bool, dele)
    rs.UnselectAllObjects()
    rs.SelectObjects( _2bool )
    rs.Command("_BooleanUnion enter enter", 1)
    rs.UnitAbsoluteTolerance( 0.00001 )
    cnt = 0
    for boo in rs.ObjectsByLayer("_booled"):
        rs.ObjectName( boo, "booled_"+str(cnt) )
        cnt+=1
        print cnt,
    return rs.ObjectsByLayer("_booled")


def OSM_buildingHeightByCurveDistance( baseCurves=[], bldgVon=0, bldgPlus=20, chkCurves=[], reverse=0, exponent=1, verbose=1):
    global maxHeight
    newEmptyLayer ("_newBuildings", [150, 110, 50])
    newEmptyLayer ("_newBuildings_crv", [250, 10, 10], parent="_newBuildings")
    newEmptyLayer ("_newBuildings_srf", [10, 10, 10],  parent="_newBuildings")
    if not baseCurves:
        baseCurves = rs.ObjectsByLayer("OSM::buildings")
        baseCurves.extend (rs.ObjectsByLayer("OSM::buildings::_bldg3D_crv"))
    distList = []
    centers  = []
    #for i, obj in enumerate(baseCurves[ bldgVon : bldgVon+bldgPlus]):
    for i, obj in enumerate(baseCurves):
        if rs.IsCurveClosed(obj):
            center = rs.CurveAreaCentroid(obj)[0] ### analog: CurveAreaCentroid(curve_id)
            centers.append(center)
            #rs.AddPoint( center )
            distances = []
            for crv in chkCurves:
                param = rs.CurveClosestPoint( crv, center )
                point = rs.EvaluateCurve( crv, param )
                dist = rs.Distance( center, point )
                distances.append( [dist, point] )
            if verbose: print i, rs.ObjectName(obj), "minDist = ", min(distances)[0]
            distList.append( [min(distances)[0], obj] )
            #rs.AddCurve( [center, min(distances)[1] ] )
    minH = min(distList)[0]
    maxH = max(distList)[0]
    
    if maxHeight < 1: maxHeight=100.1
    #print "maxHeight =", maxHeight
    cnt=0
    if 1:
        if verbose:print "len distList", len(distList)
        distList = sorted(distList)
        for entry in distList[ bldgVon : bldgVon+bldgPlus ]:
            minHig = 3.5
            hig = reMap( entry[0], minH, maxH, minHig, rand(maxHeight*.75, maxHeight) )
            hig = hig**exponent
            randX = (rand(100.0) < 15)
            hig *= 1+randX*0.5 # x% mal so hoch
            if reverse: hig /= 1+randX*0.5
            if reverse: hig = maxHeight - hig
            hig = round(hig, 2)
            if cnt==0 and verbose:
                print "minDist=",minH, ">", "maxDist=",maxH, " -- maxHeight:", maxHeight, "newHeight:",hig
            if hig < 1: hig = 1.0
            if 1 and hig>1:
                srf = rs.ExtrudeCurveStraight(entry[1], [0,0,0], [0,0,hig])
                rs.CapPlanarHoles( srf )
                nam = rs.ObjectName( entry[1] )
                tmp = nam.split("_")
                newNam = tmp[0]+"_"+tmp[1]+"_"+str(hig)+"_new"
                copy = rs.CopyObject(entry[1])
                rs.ObjectName(copy, "crv_"+tmp[1]+"_"+str(hig)+"_new")
                rs.ObjectLayer( copy, "_newBuildings::_newBuildings_crv")
                rs.ObjectName(srf, "srf_"+tmp[1]+"_"+str(hig)+"_new")
                col = reMap(entry[0], minH, maxH, 1,5)
                #rgbMat2Obj(srf, 100+10*col, 100+15*col, 100+15*col, T=0.2)
                #if randX: rgbMat2Obj(srf, 250,100,100, T=0.2)
            center = rs.CurveAreaCentroid(entry[1])[0]
            centerOben = [center[0], center[1], hig ]
            if verbose and cnt%20==0 or cnt%200==0: rs.Redraw()
            cnt+=1
    rs.CurrentLayer("Default")


def OSM_overpass_testAndChange(OSMpath, OSMfile, verbose):
    setTime()
    chgIt = 0
    with open(OSMpath+OSMfile, 'r') as fi:
        for line in fi:
            if "<note>" in line or "<meta osm_base=" in line:
                chgIt = 1
                break
    fi.close()
    if chgIt:
        lines=[]
        with open(OSMpath+OSMfile, 'r') as fi:
            for line in fi:
                if not "<note>" in line and not "<meta osm_base=" in line:
                    lines.append(line)
        fi.close()
        #rs.Sleep(3000)
        fo = open(OSMpath+OSMfile, 'w')
        for lin in lines: fo.write(lin)
        if verbose:
            print "*** file \""+OSMfile+"\" canged by OSMtestAndChange",
            print " in "+str(round(getTime(),2))+"secs"
        fo.close()



def preNull( val, sign="0", sollLen=1 ):
    # eng leading zero
    anz = sollLen-len(str(val))
    if anz>0:# and val < pow(10, anz):
        for i in range(anz): val = sign+str(val)
    return str(val)

def echo( on ):
        Rhino.ApplicationSettings.AppearanceSettings.EchoCommandsToHistoryWindow = on
        Rhino.ApplicationSettings.AppearanceSettings.EchoPromptsToHistoryWindow = on

def esc( throw_exception=1, reset=False ):
    "Tests to see if the user has pressed the escape key"
    rc = __host.EscapePressed(reset)
    #rc = sc.escape_test()
    if rc and throw_exception:
        raise Exception('\n*** esCapeKeyPressed ***')
        print "rc", rc
        return rc



######## assign a material to an object 
def rgbMat2Obj(obj_id = "", R=127, G=127, B=127, T=0.0, Gloss=0, Refl=0.0, matNam="none"):
    """ Adds Material to Objects.
    Parameters:
      obj_id = Guid of Geometry in RhinoDoc
      Red[opt] = 0-255
      Green[opt] = 0-255
      Blue[opt] = 0-255
      Transparency[opt] = 0.0 - 1.0, 1.0 being transparent
      Glossiness[opt] = 0.0 - 255.0, 255.0 being glossy
      Reflectivity[opt] = 0.0 - 1.0, 1.0 being fully reflectant
      matName[opt] = Materialname as String
    Returns:
      Material index
    """
    if obj_id: index = rs.AddMaterialToObject(obj_id)
    else: index = sc.doc.Materials.Add()
    R=int(R)
    G=int(G)
    B=int(B)
    rs.MaterialColor( index, (R, G, B) )
    rs.MaterialTransparency( index, T-0.000001 ) # T 0.0..1.0
    rs.MaterialShine( index, Gloss*255 )         # Gloss 0.0..1.0 // diag 2022 01 28
    #rs.MaterialReflectiveColor( index, (191, 191, 255) )
    newMat = sc.doc.Materials[index]
    newMat.Reflectivity=Refl
    sc.doc.Materials.Modify(newMat,index,1)
    if matNam == "none": matNam = "mat_%s_%s_%s_%s_ind_%s" % (R, G, B, int(T*100), index)
    #print "***", matNam#, #" .. created (ind", index, ")"
    rs.MaterialName (index, matNam)
    return index
    
   
######## assign a material to a layer 
def rgbMat2Lay(lay_nam = "", R=127, G=127, B=127, T=0.0, Gloss=0.0, Refl=0.0, matNam="none"):
    """ Adds Material to LAYER.
    Parameters:
      lay_nam = layerName
      Red[opt] = 0-255
      Green[opt] = 0-255
      Blue[opt] = 0-255
      Transparency[opt] = 0.0 - 1.0, 1.0 being transparent
      Glossiness[opt] = 0.0 - 1.0, 1.0 being glossy
      Reflectivity[opt] = 0.0 - 1.0, 1.0 being fully reflectant
      matName[opt] = Materialname as String
    Returns:
      Material index
    """
    if lay_nam: index = rs.AddMaterialToLayer(lay_nam)
    else: index = sc.doc.Materials.Add()
    rs.ResetMaterial(index)
    R=int(R)
    G=int(G)
    B=int(B)
    #rs.MaterialReflectiveColor( index, (191, 191, 255) )
    newMat = sc.doc.Materials[index]
    newMat.Reflectivity=Refl
    sc.doc.Materials.Modify(newMat, index, 1)
    if matNam == "none": matNam = "mat_%s_%s_%s_%s_ind_%s" % (R, G, B, int(T*100), index)
    #print "***", matNam#, #" .. created (ind", index, ")"
    rs.MaterialColor( index, (R, G, B) )
    rs.MaterialTransparency( index, T-0.000001 ) # T 0.0..1.0
    rs.MaterialShine( index, Gloss*255 )             # Gloss 0.0..1.0
    rs.MaterialName (index, matNam)
    return index
    
def printDisplay(state=1):
    if state: rs.Command("_PrintDisplay State=On Color=Display Thickness=40 _enter", 0)
    else: rs.Command("_PrintDisplay State=Off _enter", 0)


def eDup(verbose=1):
    selected = rs.SelectedObjects()
    rs.UnselectAllObjects()
    vorher = len(rs.AllObjects())
    rs.Command("_selDup", 0)
    if selected and rs.SelectedObjects():
        for obj in rs.SelectedObjects():
            if obj in selected: selected.remove( obj )
    rs.DeleteObjects( rs.SelectedObjects() )
    if selected: rs.SelectObjects(selected)
    if verbose: print "*** dm.eDup() deleted", vorher - len(rs.AllObjects()), "objects ***\n"

def eAA():
    """Deletes Absolutely All objects, incl hidden and locked
    """
    rs.UnlockObjects(rs.AllObjects())
    rs.ShowObjects  (rs.AllObjects())
    rs.DeleteObjects(rs.AllObjects())
    rs.Redraw()

eDup(0)
echo(0)

###___GPX-----------------------------------------------------------------------



################
######## date/time/location- and osm-related functions
################

######## adding leading zeros
def textPre0( val, sign="0", sollLen=1 ):
    # eng leading zero
    anz = sollLen-len(str(val))
    if anz>0:# and val < pow(10, anz):
        for i in range(anz): val = sign+str(val)
    return str(val)

def ortX(ortX):
    ort = "guinea"
    lat = 0#
    lon = 0#
    timeZone = 0#
    ### graz
    if ortX == "graz": 
        ort = "graz"
        lat = 47.0654 #=kronesgasse / opernring 47.07086780
        lon = 15.4486 #                         15.43827860
        timeZone = 1
    if ortX == "kollerweg":
        ort = "kollerweg"
        lat = 47.130380844984266 # lt google maps
        lon = 15.518638635469191 
        timeZone = 1
    ### NYC, UN headquaters
    if ortX == "NYC UN": 
        lat = 40.7492161
        lon = -73.96867250
        timeZone = -5
    ### stonehenge, UK
    if ortX == "stonehenge": 
        lat = 51.178889
        lon = -1.826111
        timeZone = 0
    ### gizeh, egypt
    if ortX == "giza": ### cairo 30 2 N .. 31 14 E
        ort == "giza"
        lat = 31.1241000
        lon = 29.9694000
        timeZone = 2
    if ortX == "koenigsberg": 
        ort == "koenigsberg"
        lat = 54.6969000
        lon = 20.4943000
        timeZone = 2
    if ortX == "shepperton": 
        ort == "shepperton"
        lat = 51.3928000
        lon = -0.4964000
        timeZone = 0
    if ortX == "barcelona": 
        ort == "barcelona"
        lat = 41.3737000
        lon = 2.1133000
        timeZone = 1
    return [lat, lon, timeZone]


def daysInMonth(year):
    return [31, 28+(int(year)%4==0), 31,30,31,30,31,31,30,31,30,31]

def number2date( number=31, year=2021):
    months = daysInMonth(year)
    for i in range(0, 11):
        if number <= months[i]:
            return [i+1, number]
        number -= months[i]
        if number <= months[i+1]:
            return [i+2, number]
#print number2date( number=32+28, year=2020)

def date2number( year=2020, mon=12, day=30, verbose=0 ):
    if type(year)==list:
        return date2number( year[0], year[1], year[2], verbose=0 )
    months = daysInMonth(year)
    if day > daysInMonth(year)[int(mon)-1]:
        if verbose: print "*** ________no good date: date2number("+str(year)+(", ")+str(mon)+", "+str(day)+") | return =",
        return 0
    number = 0
    for m in range(1, int(mon)): number += months[m-1]
    return number+int(day)
#print date2number(year=2020, mon=2, day=31)

def getDateNow():
    now = datetime.now()
    #print "day :", type(now.day), now.day
    return [now.year, now.month, now.day, now.hour, now.minute, now.second]

_2day = getDateNow()
dayNumber2day = date2number(_2day[0], _2day[1], _2day[2])


dlSaving=0 # sommerzeit o.a.
dat = getDateNow()

def setSun ( year=dat[0], mon=dat[1], day=dat[2], hour=dat[3], min=dat[4], sec=dat[5], verbose=0):
    sun = sc.doc.Lights.Sun
    sun.Enabled = 1
    sun.TimeZone = ortX(ort)[2]
    sun.DaylightSavingMinutes = dlSaving
    if day <= daysInMonth(year)[mon-1]:
        sun.SetPosition(System.DateTime(year, mon, day, hour, min, sec), ortX(ort)[0],ortX(ort)[1])
        if verbose:
            print "||| setSun @", year, textPre0(mon, sign="0", sollLen=2), textPre0(day, sign="0", sollLen=2), "(day# "+str(date2number(year,mon,day))+")","@", str(hour)+":"+str(min)+":"+str(sec)+" @ "+ort.upper()+" @ lat/lon =",str(round(ortX(ort)[0],4))+"°/"+str(round(ortX(ort)[1],4))+"°", "| tZ = "+str(ortX(ort)[2]),
            print "| azi/alti =", str(round(sun.Azimuth,4))+"°/"+str(round(sun.Altitude,4))+"°"#,":"
            #print "*** [ azimut", round(sun.Azimuth,2), " / altitude",round(sun.Altitude,2),"/ sunVector [", sun.Vector, "] ]"
        #return [ sun.Azimuth, sun.Altitude, [sun.Vector[0], sun.Vector[1], sun.Vector[2]] ]
        sunVec = sun.Vector
        return [ sun.Azimuth, sun.Altitude, sun.Vector ]
    else:
        print "*** ________no good date: setSun("+str(year)+(", ")+str(mon)+", "+str(day)+") | return =",
        return [0,0,[0,0,0]]

ort = "graz"
setSun(verbose=0)
rs.EnableRedraw(0)
####################################################################
### version = "'''  ver 20221201 by _diag 4 DM* @ I OI III @ TUGraz '''"     #
version = "||| CC by-nc-sa agruber@tugraz.at ||| 4 DM* @ I OI III ||| ver 20231201"     #
print     "|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||"
print     "||| OSM_lib imported in "+ str(getTime(0)) +"s", version
print     "|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||"
####################################################################
sun = sc.doc.Lights.Sun
sun.Enabled = 1