| #!BPY
 """ Registration info for Blender menus
 Name: 'KMZ/KML import''
 Blender: 241
 Group: 'Import'
 Tip: 'Import geometry of .kml or .kmz 3D models'
 """
 __author__ = "Jean-Michel Soler (jms)"
 __version__ = "0.1.9, august, 11th, 2006"
 __bpydoc__ = """\
        To read 3d geometry .kmz and .kml
file 
        Caution  : the geometry data
of the Google Earth's files on the web 
        are licended  and you can
not load or use it in a personnal work .
        Be aware that the containt of
the file you try to read must be free 
        or legaly your's .
   Attention, this script uses the Blender's intern fill() function
to create 
   certain complex faces but to work correctly this function 
needs a few 
   conditions :
   1/ At least one 3D window must be open in the the work space
   2/ Work space must be set in "Global"  (the layer panel
can be saw in the blender task bar, it 's ok)  and not in "Local"
   3/ You have to make the import in Object modebut the vertex mode
must be active. 
  
 """
 # --------------------------------------------------------------------------
 # ***** BEGIN GPL LICENSE BLOCK *****
 #
 # Copyright (C) 2006: jm soler, jmsoler_at_free.fr
 #
 # This program is free software; you can redistribute it and/or
 # modify it under the terms of the GNU General Public License
 # as published by the Free Software Foundation; either version 2
 # of the License, or (at your option) any later version.
 #
 # This program is distributed in the hope that it will be useful,
 # but WITHOUT ANY WARRANTY; without even the implied warranty of
 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 # GNU General Public License for more details.
 #
 # You should have received a copy of the GNU General Public License
 # along with this program; if not, write to the Free Software Foundation,
 # --------------------------------------------------------------------------
 import Blender
 from Blender  import Window
 import sys
 from Blender import Mathutils
 BLversion=Blender.Get('version')
 try:
  import nt
  os=nt
  os.sep='\\'
 except: 
  import posix
  os=posix
  os.sep='/'
 def isdir(path):
  try:
   st = os.stat(path)
   return 1 
  except:
   return 0
 def split(pathname):
  if pathname.find(os.sep)!=-1:
   k0=pathname.split(os.sep)
  else:
   if os.sep=='/':
    k0=pathname.split('\\')
   else:
    k0=pathname.split('/') 
  directory=pathname.replace(k0[len(k0)-1],'')
  Name=k0[len(k0)-1]
  return directory, Name
 def join(l0,l1): 
  return  l0+os.sep+l1
 os.isdir=isdir
 os.split=split
 os.join=join
 def fonctionSELECT(nom):
  scan_FILE(nom)
 def filtreFICHIER(nom):
  """
  Function  filtreFICHIER
  in  : string  nom , filename
  out : string of data   , if correct filecontaint 
  """
  if nom.upper().find('.KMZ')!=-1:
   from zipfile import ZipFile, ZIP_DEFLATED
   tz=ZipFile(nom,'r',ZIP_DEFLATED)
   t = tz.read(tz.namelist()[0])
   tz.close()
   t=t.replace('<br>','') 
   return t
  elif nom.upper().find('.KML')!=-1:
   tz=open(nom,'r')
   t = tz.read()
   tz.close()
   t=t.replace('<br>','')
   return t
  else : 
   return None
 # ....
 # needed to global call
 # ....
 OB = None 
 SC = None 
 ME=  None
 POLYGON_NUMBER=0
 DOCUMENTORIGINE=[]
 UPDATE_V=[]
 UPDATE_F=[]
 POS=0
 NUMBER=0
 PLACEMARK=0
 eps=0.0000001
 npoly=0
 gt1=Blender.sys.time()
 def create_LINE(BROKEN_LINE,tv):
  me = Blender.Mesh.New('myMesh') 
  ob = Blender.Object.New('Mesh','myObj') 
  ob.link(me)
  sc = Blender.Scene.GetCurrent() 
  sc.link(ob)
  v=me.verts
  e=me.edges
  for bl in BROKEN_LINE:
   #print bl
   v.extend(bl)
   e.extend(v[-2],v[-1])
   v[-2].sel=1
   v[-1].sel=1 
  if tv :
   me.remDoubles(0.0001)
 def cree_POLYGON(ME,TESSEL):
  global  OB, npoly, UPDATE_V, UPDATE_F, POS
  npoly+=1 
  for T in TESSEL: del T[-1]
  if npoly %100 == 1 : 
   print 'Pgon: ', npoly, 'verts:',[len(T) for T in TESSEL]
  if npoly %250 == 1 : 
   Blender.Window.RedrawAll()
   g2= Blender.sys.time()-gt1
   print int(g2/60),':',int(g2%60) 
  if len(TESSEL)==1 and len(TESSEL[0]) in [3,4] :
   if not UPDATE_F:
      POS=len(ME.verts)
   for VE in TESSEL[0]:
       UPDATE_V.append(VE)
   if len(TESSEL[0])==3:
      UPDATE_F.append([POS,POS+1,POS+2])
      POS+=3
   else :
      UPDATE_F.append([POS,POS+1,POS+2,POS+3])
      POS+=4
  else :
   if UPDATE_V : ME.verts.extend(UPDATE_V)
   FACES=[]
   if UPDATE_F:
    for FE in UPDATE_F:
     if len(FE)==3:
      FACES.append([ME.verts[FE[0]],ME.verts[FE[1]],ME.verts[FE[2]]]) 
     else :
      FACES.append([ME.verts[FE[0]],ME.verts[FE[1]],ME.verts[FE[2]],ME.verts[FE[3]]])
   if FACES:
     ME.faces.extend(FACES)
     FACES=[]
   UPDATE_F=[]
   UPDATE_V=[]
   EDGES=[]
   for T in TESSEL: 
    ME.verts.extend(T)
    for t in range(len(T),1,-1):
       ME.verts[-t].sel=1
       EDGES.append([ME.verts[-t],ME.verts[-t+1]])
    ME.verts[-1].sel=1
    EDGES.append([ME.verts[-1],ME.verts[-len(T)]])
    ME.edges.extend(EDGES)
   ME.fill()
   if npoly %500 == 1 :
    for v in ME.verts:
     v.sel=1
    ME.remDoubles(0.0)
   for v in ME.verts:
       v.sel=0
  TESSEL=[] 
  return ME,TESSEL
 def cree_FORME(v,TESSEL):
    VE=[(v[0]-DOCUMENTORIGINE[0])* 85331.2,
        (v[1]-DOCUMENTORIGINE[1])* 110976.0,
        (v[2]-DOCUMENTORIGINE[2]) 
]
    TESSEL.append(VE)
 def active_FORME():
  global ME, UPDATE_V, UPDATE_F, POS, OB
  if len(UPDATE_V)>2 :
   #print UPDATE_V
   ME.verts.extend(UPDATE_V)
   FACES=[]
   #print UPDATE_F,  len(UPDATE_F)
   for FE in UPDATE_F:
    #print FE
    if len(FE)<4:
     FACES.append([ME.verts[FE[0]],ME.verts[FE[1]],ME.verts[FE[2]]]) 
    else :
     FACES.append([ME.verts[FE[0]],ME.verts[FE[1]],ME.verts[FE[2]],ME.verts[FE[3]]])
    #if len(ME.faces)%200==1 : print len(ME.faces) 
   if FACES: 
    ME.faces.extend(FACES)
  UPDATE_V=[]
  UPDATE_F=[]
  POS=0
  if len(ME.verts)>0:
   for v in ME.verts:
    v.sel=1
   ME.remDoubles(0.0)
 def wash_DATA(ndata):
  if ndata!='':
   ndata=ndata.replace('\n',',')
   ndata=ndata.replace('\r','') 
   while ndata[-1]=='  ': 
    ndata=ndata.replace('  ',' ')
   while ndata[0]==' ': 
    ndata=ndata[1:]
   while ndata[-1]==' ': 
    ndata=ndata[:-1] 
   if ndata[0]==',':ndata=ndata[1:]
   if ndata[-1]==',':ndata=ndata[:-1]
   ndata=ndata.replace(',,',',') 
   ndata=ndata.replace(' ',',')
   ndata=ndata.split(',')
   for n in ndata :
    if n=='' : ndata.remove(n) 
  return  ndata 
 def collecte_ATTRIBUTS(data):
   data=data.replace('  ',' ')
   ELEM={'TYPE':data[1:data.find(' ')]}
   t1=len(data)
   t2=0
   ct=data.count('="')
   while ct>0:
    t0=data.find('="',t2)
    t2=data.find(' ',t2)+1
    id=data[t2:t0]
    t2=data.find('"',t0+2)
    if id!='d':
     exec "ELEM[id]=\"\"\"%s\"\"\""%(data[t0+2:t2].replace('\\','/'))
    else:
     exec "ELEM[id]=[%s,%s]"%(t0+2,t2) 
    ct=data.count('="',t2)
   return ELEM
 def contruit_HIERARCHIE(t,tv=0):
  global DOCUMENTORIGINE, OB , ME, SC 
  global NUMBER, PLACEMARK, POLYGON_NUMBER
  vv=[]
  TESSEL=[]
  BROKEN_LINE=[]
  OB = Blender.Object.New('Mesh') 
  SC.link(OB)
  ME= OB.getData(mesh=1)
  [O.select(0) for O in Blender.Scene.getCurrent().getChildren()]
  OB.select(1)
  t=t.replace('\t',' ')
  while t.find('  ')!=-1:
        t=t.replace('  ',' ')
  n0=0 
  t0=t1=0
  baliste=[]
  balisetype=['?','?','/','/','!','!']
  BALISES=['D',  #DECL_TEXTE',
       'D',  #DECL_TEXTE',
       'F',  #FERMANTE',
       'E',  #ELEM_VIDE',
       'd',  #DOC',
       'R',  #REMARQUES',
       'C',  #CONTENU',
       'O'   #OUVRANTE'
       ]
  TAGS = ['kml','Document','description','DocumentSource',
    'DocumentOrigin','visibility','LookAt',
   'Folder','name','heading','tilt','latitude',
   'longitude','range','Style','LineStyle','color',
   'Placemark','styleUrl','GeometryCollection',
   'Polygon','LinearRing','outerBoundaryIs',
   'altitudeMode','coordinates','LineString',
    'fill','PolyStyle','outline','innerBoundaryIs',
    'IconStyle', 'Icon', 'x','y' ,'w','href','h'
    ]
  STACK=[]
  latitude = float(t[t.find('<latitude>')+len('<latitude>'):t.find('</latitude>')])
  longitude = float(t[t.find('<longitude>')+len('<longitude>'):t.find('</longitude>')])
  DOCUMENTORIGINE=[longitude,latitude,0 ]
  GETMAT=0
  MATERIALS=[M.getName() for M in Blender.Material.Get()]
  while t1<len(t) and t0>-1 :
   t0=t.find('<',t0)
   t1=t.find('>',t0)
   ouvrante=0
   if t0>-1 and t1>-1:
    if t[t0+1] in balisetype:
     b=balisetype.index(t[t0+1])
     if t[t0+2]=='-': 
       b=balisetype.index(t[t0+1])+1
     balise=BALISES[b]
     #print STACK
     if b==2 and t[t0:t1].find(STACK[-1])>-1:
         parent=STACK.pop(-1)
    elif t[t1-1] in balisetype:
     balise=BALISES[balisetype.index(t[t1-1])+1]
    else:
     t2=t.find(' ',t0) 
     if t2>t1: t2=t1
     ouvrante=1
     NOM=t[t0+1:t2]
     if t.find('</'+NOM)>-1:
        balise=BALISES[-1]
     else:
        balise=BALISES[-2]
    if balise=='E' or balise=='O':
     if NOM not in TAGS :
      if NOM not in ['a','b','table','tr','td','div','hr']:
       TAGS.append(NOM)
      else :
       t0=t.find('</'+NOM,t0)
       t1=t.find('>',t0)
       if t0==-1 and t1==-1:
            break
     if  balise=='O' and NOM in TAGS: 
      STACK.append(NOM)
      if not PLACEMARK :
       if NOM.find('Style')==0:
        proprietes=collecte_ATTRIBUTS(t[t0:t1+ouvrante])
        #print proprietes
       if NOM.find('PolyStyle')==0: 
         GETMAT=1
       if NOM.find('color')==0 and GETMAT:
         COLOR=t[t2+1:t.find('</color',t2)]
         #print COLOR
         COLOR=[eval('0x'+COLOR[0:2]),
eval('0x'+COLOR[2:4]), eval('0x'+COLOR[4:6]), eval('0x'+COLOR[6:])]
         #print COLOR 
         if 'id' in proprietes.keys()
and proprietes['id'] not in MATERIALS:
          MAT=Blender.Material.New(proprietes['id'])
          MAT.rgbCol = [COLOR[3]/255.0,COLOR[2]/255.0,COLOR[1]/255.0] 
          MAT.setAlpha(COLOR[0]/255.0)
          MATERIALS.append(MAT.getName())
         GETMAT=0
      if NOM.find('Polygon')>-1:
       VAL=t[t2+2:t.find('</Polygon',t2)]
       n=VAL.count('<outerBoundaryIs>')+VAL.count('<innerBoundaryIs>')
      if NOM.find('LineString')>-1:
       VAL=t[t2+2:t.find('</LineString',t2)]
       #n=VAL.count('<outerBoundaryIs>')+VAL.count('<innerBoundaryIs>')
       #print STACK
      if NUMBER and NOM.find('Placemark')>-1 :
       PLACEMARK=1
       if t[t2:t.find('</Placemark',t2)].find('Polygon')>-1
and len(ME.verts)>0:
        active_FORME()
        OB.select(0) 
        #[O.select(0) for O in Blender.Object.Get()]
        OB = Blender.Object.New('Mesh') 
# link mesh to an object
        SC = Blender.Scene.GetCurrent()         
# link object to current scene
        SC.link(OB)
        ME=OB.getData(mesh=1)
        OB.select(1) 
      if NOM.find('styleUrl')>-1:
        material= t[t2+2:t.find('</styleUrl',t2)]
        if material in MATERIALS :
         ME.materials=[Blender.Material.Get(material)]
        else :
         SMat=t.find('<Style id="'+material)
         if SMat>-1 :
          SMatF=t.find('</Style',SMat)
          SPolSt=t[SMat:SMatF].find('<PolyStyle>')
          if t[SMat:SMatF].find('<color>',SPolSt)>-1:
           COLOR=t[SMat:SMatF][t[SMat:SMatF].find('<color>',SPolSt)+7:t[SMat:SMatF].find('</color>',SPolSt)]
           if len(COLOR)>0
: COLOR=[eval('0x'+COLOR[0:2]), eval('0x'+COLOR[2:4]), eval('0x'+COLOR[4:6]),
eval('0x'+COLOR[6:])]
          else :
           #print material,'\n\n'
           COLOR=[255,255,255,255]
          MAT=Blender.Material.New(material)
          MAT.rgbCol = [COLOR[3]/255.0,COLOR[2]/255.0,COLOR[1]/255.0] 
          MAT.setAlpha(COLOR[0]/255.0)
          MATERIALS.append(MAT.getName())
          ME.materials=[Blender.Material.Get(material)]
      if NOM.find('coordinates')>-1:
       VAL=t[t2+2:t.find('</coordinates',t2)]
       if STACK[-2]=='DocumentOrigin' :
        DOCUMENTORIGINE=[float(V) for
V in  VAL.replace(' ','').replace('\n','').split(',')]
       if STACK[-2]=='LinearRing'  :
        n-=1
        TESSEL.append([])
        VAL=wash_DATA(VAL) 
        vv=[[float(VAL[a+ii]) for ii in
xrange(3)] for a in xrange(0,len(VAL),3)]
        if vv  : [cree_FORME(v,TESSEL[-1])
for v in vv]
        del VAL
        if n==0: ME,TESSEL= cree_POLYGON(ME,TESSEL)
       if STACK[-2]=='LineString'  :
        #print STACK, STACK[-2] 
        BROKEN_LINE.append([])
        VAL=wash_DATA(VAL) 
        vv=[[float(VAL[a+ii]) for ii in
xrange(3)] for a in xrange(0,len(VAL),3)]
        if vv  : [cree_FORME(v,BROKEN_LINE[-1])
for v in vv]
        del VAL
     D=[] 
   else:
      break
   t1+=1
   t0=t1 
  print 'BROKEN_LINE', BROKEN_LINE 
  if BROKEN_LINE :
   create_LINE(BROKEN_LINE,tv)
 def scan_FILE(nom):
   global NUMBER, PLACEMARK, SC, OB, ME, POLYGON_NUMBER
   dir,name=split(nom)
   name=name.split('.')
   result=0
   t=filtreFICHIER(nom)
   tag1 = Blender.Draw.Create(0)
   tag2 = Blender.Draw.Create(0)
   block = []
   block.append("Import from 2D source")
   block.append(("Force 2D import", tag1, "if no polygon found
in the file"))
   block.append(("Remove double ", tag2, " "))
   POLYGON_NUMBER=t.count('<Polygon')
   print 'Number of Polygons :  ', POLYGON_NUMBER
   if POLYGON_NUMBER==0 :
    retval = Blender.Draw.PupBlock("KML/KMZ import", block)
    if not tag1.val :
     name = "WARNING %t| Sorry, the script can\'t find
any geometry in this file ."  # if no %xN int is set, indices start
from 1
     result = Blender.Draw.PupMenu(name)
     print '#----------------------------------------------'
     print '#  Sorry the script can\'t find any
geometry in this' 
     print '#  file .'
     print '#----------------------------------------------'
     Blender.Window.RedrawAll()
     return
    else :
     SC = Blender.Scene.GetCurrent()
     PLACEMARK_NUMBER=t.count('<Placemark>')
     print 'Number of Placemark   :  ',
PLACEMARK_NUMBER
     if PLACEMARK_NUMBER!=POLYGON_NUMBER :
      NUMBER=1
      PLACEMARK=0
     if t!='false':
      contruit_HIERARCHIE(t,tag2.val)
      #active_FORME()
   else:
    SC = Blender.Scene.GetCurrent()
    PLACEMARK_NUMBER=t.count('<Placemark>')
    print 'Number of Placemark   :  ', PLACEMARK_NUMBER
    if PLACEMARK_NUMBER!=POLYGON_NUMBER :
     NUMBER=1
     PLACEMARK=0
    if t!='false':
     contruit_HIERARCHIE(t)
     active_FORME()
   gt2=Blender.sys.time()-gt1
   print int(gt2/60),':',int(gt2%60) 
 Blender.Window.FileSelector (fonctionSELECT, 'SELECT a .KMZ FILE')  |