viernes, 20 de abril de 2012

Python y ArcObjects – (2da Parte)

En este post se hablara en cómo usar los COMTYPES para nuestras aplicaciones en Python. Los COMTYPES nos ayudaran en usar en cierta manera las funciones propias que tiene el ArcObject (y en realidad en muchas aplicaciones como Excel, Autocad, etc),  teniendo así más recursos que usar al momento de programar.

Para esto, usaremos como referencia este documento  (las palabras en negrita y subrayadas a la vez estan los enlaces que se menciona) que está en la red:
http://www.pierssen.com/arcgis/upload/misc/python_arcobjects.pdf
Que nos detalla algo de la funcionalidad de los COMTYPES
En este mismo documento nos dice en donde conseguirlo e instalarlo y también una descripción y definición de los mismos (que están en este enlace http://starship.python.net/crew/theller/comtypes/).

 
COMTYPES tiene 2 módulos que en este momento explicaremos: GetModule, CreateObject y una función: QueryInterface que igualmente explicaremos.
Usamos GetModule si queremos obtener una clase o un objeto del ArcObjects.
Usamos CreateObject si queremos crear o hacer un objeto para un propósito.
Usaremos QueryInterface cuando necesitamos asignar una variable “Implícitamente” a otra (esto lo veremos con mejor detalle en el ejemplo)
Ejemplo de código:



Para un mayor entendimiento,  leeremos algunas propiedades de una vista y de un shape usando ArcObjects y luego en Python con Comtypes para ver la semejanza entre ambos. Está dividido en partes para notar las semejanzas de las funciones. Se utilizara las mismas variables para un mayor entendimiento de las semejanzas y diferencias:

Código en ArcObjects:


Sub LeerPropiedadesVista()
   
  'INICIO: PARTE A
  Dim MiProyMxd As IMxDocument 'declaro una variable que es del tipo "proyecto.mxd"
  Set MiProyMxd = ThisDocument 'inicializacion de la variable, asigno a la variable creada a este documento abierto
  Set MiProyMxd = Application.Document
  'FIN: PARTE A
 
    'INICIO: PARTE B
  Dim MiMapa As IMap
  Set MiMapa = MiProyMxd.FocusMap
  MsgBox "Numero de Shapes: " + Str(MiMapa.LayerCount)
  'FIN: PARTE B
 
    'INICIO: PARTE C
  Dim MiShape As IFeatureLayer 'declaro una variable donde guardo el shape a ingresar
  Set MiShape = New FeatureLayer 'inicializacion de la variable
  Set MiShape = MiProyMxd.SelectedLayer
  'FIN: PARTE C
 
 
  'INICIO: PARTE D
  If MiShape Is Nothing Then
    MsgBox "no hay un solo Layer seleccionado. Por favor seleccione solo UNO"
  Else
    MsgBox "Shape seleccionado: " + MiShape.Name
  End If
  'FIN: PARTE D
 
  'INICIO: PARTE E
  MsgBox "Projección del mapa: " + MiMapa.SpatialReference.Name
  'FIN: PARTE E
End Sub



Sub LeerPropiedadesShape()


  Dim MiProyMxd As IMxDocument 'declaro una variable que es del tipo "proyecto.mxd"
  Set MiProyMxd = ThisDocument 'inicializacion de la variable, asigno a la variable creada a este documento abierto
 
  If Not TypeOf MiProyMxd.ActiveView Is IMap Then 'Control de error: si un mapa no esta activado, finaliza la macro
    MsgBox "Por favor, un mapa debe estar activado"
    Exit Sub
  End If
 
  'INICIO: PARTE F
  Dim MiShape As IFeatureLayer 'declaro una variable donde guardo el shape a ingresar
  Set MiShape = New FeatureLayer 'inicializacion de la variable
  Set MiShape = MiProyMxd.SelectedLayer
  If MiShape Is Nothing Then
    MsgBox "Seleccione un Shape"
    Exit Sub
  End If
  MsgBox "Nombre Shape: " + MiShape.Name
  'FIN: PARTE F
 
 
  'INICIO: PARTE G
  Dim MIFeatureLayer As IFeatureLayer
  Set MIFeatureLayer = MiShape
 
  Dim MiFeatureClass As IFeatureClass
  Set MiFeatureClass = MIFeatureLayer.FeatureClass

  If MiFeatureClassShapeType = esriGeometryPoint Then
    MsgBox "Topologia Shape: Punto"
  ElseIf MiFeatureClass.ShapeType = esriGeometryPolyline Then
    MsgBox "Topologia Shape: Polilinea"
  ElseIf MiFeatureClass.ShapeType = esriGeometryPolygon Then
    MsgBox "Topologia Shape: Poligono"
  End If
  'FIN: PARTE G
 
 
  'INICIO: PARTE H
  MsgBox "Tipo de dato: " + MIFeatureLayer.DataSourceType
  'FIN: PARTE H
 
 
  'INICIO: PARTE I
  Dim MiFields As IFields
  Set MiFields = New Fields
  Set MiFields = MiFeatureClass.Fields
  MsgBox "Numero de columnas: " + Str(MiFields.FieldCount)
  'FIN: PARTE I
 
 
 
  'INICIO: PARTE J
  Dim MiQuery As IQueryFilter
  Set MiQuery = New QueryFilter
  MiQuery.AddField ("Nothing")
  MsgBox "Numero de Objetos: " + Str(MiFeatureClass.FeatureCount(MiQuery))
  'FIN: PARTE J
 


 'INICIO: PARTE K
 Dim MiGeoDataSet As IGeoDataset
 Dim MiSpatialReference As ISpatialReference
 Set MiGeoDataSet = MiShape 'QI for the geodatset from the layer
 Set MiSpatialReference = MiGeoDataSet.SpatialReference
 MsgBox "Datum: " + MiSpatialReference.Name
'FIN: PARTE K

End Sub


Código en Python - Comtypes:

from comtypes.client import GetModule, CreateObject

esricarto = GetModule ("C:\Program Files\ArcGIS\com\esricarto.olb") ## Clases:IMap, ILayer, IFeatureLayer
esriGeoDatabase = GetModule ("C:\Program Files\ArcGIS\com\esriGeoDatabase.olb") ## Clases:IFeatureClass, IFieldEdit
esriArcMapUI = GetModule ("C:\Program Files\ArcGIS\com\esriArcMapUI.olb") ## Clases: IApplication, IMxDocument
esriFramework = GetModule ("C:\Program Files\ArcGIS\com\esriFramework.olb")## Clases: IAppROT
esriGeoDataset= GetModule ("C:\Program Files\ArcGIS\com\esriGeoDatabase.olb")## Clases: ISpatialReference

#Sub LeerPropiedadesVista()

#INICIO: PARTE A
pAppROT = CreateObject(esriFramework.AppROT, interface=esriFramework.IAppROT)
print "Cantidad de Aplicaciones ARCGIS abiertas: " , pAppROT.Count ## CUENTA CUANTAS APLICACIONES ARCGIS HAY
Application = pAppROT.Item(0)
print  "1era Aplicación ARCGIS abierta: " , Application.Name # NOMBRA LA PRIMERA APLICACION QUE FUE ABIERTA
MiProyMxd_TMP = Application.Document # Set MiProyMxd = Application.Document
MiProyMxd =  MiProyMxd_TMP.QueryInterface(esriArcMapUI.IMxDocument)
#FIN: PARTE A


#INICIO: PARTE B
MiMapa = MiProyMxd.FocusMap #Set MiMapa = MiProyMxd.FocusMap
print "Numero de Shapes: " , MiMapa.LayerCount
#FIN: PARTE B


#INICIO: PARTE C
MiShape = MiProyMxd.SelectedLayer
#FIN: PARTE C


#INICIO: PARTE D
if not MiShape:
    print "no hay un solo Layer seleccionado. Por favor seleccione solo UNO"

else:
    print "Shape seleccionado: " , MiShape.Name
#FIN: PARTE D


#INICIO: PARTE E   
print "Projección del mapa:  " , MiMapa.SpatialReference.Name
#FIN: PARTE E


#Sub LeerPropiedadesShape()


#INICIO: PARTE F 
MiShape = MiProyMxd.SelectedLayer
if not MiShape:
   print "Seleccione un Shape"
   #return None
print "Nombre Shape: " , MiShape.Name
#FIN: PARTE F


#INICIO: PARTE G
MiFeatureLayer = MiShape.QueryInterface(esricarto.IFeatureLayer)
MiFeatureClass = MiFeatureLayer.FeatureClass
if MiFeatureClass.ShapeType == 1: #"esriGeometryPoint":
    print "Topologia Shape: Punto"
elif MiFeatureClass.ShapeType == 3: #"esriGeometryPolyline":
    print "Topologia Shape: Polilinea"
elif MiFeatureClass.ShapeType == 4: #  "esriGeometryPolygon":
    print "Topologia Shape: Poligono"
#FIN: PARTE G


#INICIO: PARTE H   
print "Tipo de dato: " , MiFeatureLayer.DataSourceType
#FIN: PARTE H



#INICIO: PARTE I
MiFields = MiFeatureClass.Fields
print "Numero de columnas: " , MiFields.FieldCount
#FIN: PARTE I



#INICIO: PARTE J
MiQuery = CreateObject (esriGeoDatabase.QueryFilter, interface = esriGeoDatabase.IQueryFilter)
MiQuery.AddField("Nothing")
print "Numero de Objetos: " , MiFeatureClass.FeatureCount(MiQuery)
#FIN: PARTE J



#INICIO: PARTE K
MiGeoDataSet = MiShape.QueryInterface(esriGeoDataset.IGeoDataset)
MiSpatialReference = MiGeoDataSet.SpatialReference
print "Datum: " , MiSpatialReference.Name
#FIN: PARTE K





Vemos que tienen muchas similitudes, pero ahora hablaremos de las diferencias, sobre todo en la parte de asignar una variable.
Parte A:

ArcObject

1)    Dim MiProyMxd As IMxDocument
2)    Set MiProyMxd = ThisDocument
3)    Set MiProyMxd = Application.Document


Python – Comtypes

4)    pAppROT = CreateObject(esriFramework.AppROT, interface=esriFramework.IAppROT)5)    print "Cantidad de Aplicaciones ARCGIS abiertas: " , pAppROT.Count
6)    Application = pAppROT.Item(0)
7)     print  "1era Aplicación ARCGIS abierta: " , Application.Name
8)    MiProyMxd_TMP = Application.Document
9)    MiProyMxd =  MiProyMxd_TMP.QueryInterface(esriArcMapUI.IMxDocument)


En ArcObject: creo que esta es la forma normal de declarar nuestra aplicación ArcMap para leer sus propiedades.

En Python: como se ve, es algo largo declarar una variable para leer nuestra primera aplicación activa que fue ejecutada (ArcMap), sin contar las líneas 5 y 7 que son salidas que nos muestran información, hay 4 líneas de código efectivas para obtener lo que en ArcObjects lo tenemos en 3 líneas efectivas. Vemos que creamos un objeto  pAppROT de tipo AppROT(que nos permite manipular aplicaciones de ESRI) con CreateObject en la línea 4, luego en la línea 6 leemos la primera aplicación ArcGIS abierta que en este caso es el ArcMap y lo guardamos en la variable Application de tipo Application, luego en la línea 8 asignamos a la variable MiProyMxd_TMP de tipo  Document, y finalmente en la línea 9 queremos que nuestra variable MiProyMxd sea de tipo ImxDocument a partir de la variable MiProyMxd_TMP , pero explícitamente no lo podemos hacer directamente, nos dará un error. Así que usaremos la función QueryInterface que nos ayuda a asignar variables de un tipo a otro.

De forma grafica, vemos cual es la secuencia de lo explicado arriba:


Parte G:

ArcObject

1)  Dim MIFeatureLayer As IFeatureLayer
2) Set MIFeatureLayer = MiShape
3)  Dim MiFeatureClass As IFeatureClass
4) Set MiFeatureClass = MIFeatureLayer.FeatureClass
5) If MiFeatureClassShapeType = esriGeometryPoint Then
6)    MsgBox "Topologia Shape: Punto"
7) ElseIf MiFeatureClass.ShapeType = esriGeometryPolyline Then
8)     MsgBox "Topologia Shape: Polilinea"
9) ElseIf MiFeatureClass.ShapeType = esriGeometryPolygon Then
10)     MsgBox "Topologia Shape: Poligono"
11)End If

 


Python – Comtypes

12) MiFeatureLayer = MiShape.QueryInterface(esricarto.IFeatureLayer)
13) MiFeatureClass = MiFeatureLayer.FeatureClass
14) if MiFeatureClass.ShapeType == 1: #"esriGeometryPoint":
 15)       print "Topologia Shape: Punto"
16) elif MiFeatureClass.ShapeType == 3: #"esriGeometryPolyline":
 17)       print "Topologia Shape: Polilinea"
18) elif MiFeatureClass.ShapeType == 4: #  "esriGeometryPolygon":
 19)       print "Topologia Shape: Poligono"


En ArcObject: estas sentencias hacen que se asigna un ILayer como IFeatureLayer y luego este ultimo como un IFeatureClass.

En Python: en la línea 12, tenemos la variable MiShape, que viene ser como un ILayer (proveniente de la PARTE F: MiShape = MiProyMxd.SelectedLayer), y queremos crear la variable MiFeatureLayer  de tipo IFeatureLayer, pero si buscamos en el “Object Browser” de ArcObjects no hay propiedad, método u objeto parecido a FeatureLayer en ILayer (ver en la imagen de abajo).

Para resolver este problema, usamos otra vez el QueryInterface que nos permite como en la PARTE A, crear la variable MiFeatureLayer como un IFeatureLayer proveniente desde la variable MiShape que es un ILayer.

En la línea 13, creamos la variable MiFeatureClass que es de tipo IFeatureClass proveniente de la variable MiFeatureLayer, pero no usaremos el QueryInterface ya que IFeatureLayer tiene un objeto de tipo IFeatureClass como se muestra en el “Object Browser” de ArcObjects.


De forma grafica, vemos cual es la secuencia de lo explicado arriba:

 
Parte K:

ArcObject

1) Dim MiGeoDataSet As IGeoDataset
2) Dim MiSpatialReference As ISpatialReference
3) Set MiGeoDataSet = MiShape
4) Set MiSpatialReference = MiGeoDataSet.SpatialReference
5) MsgBox "Datum: " + MiSpatialReference.Name

 


Python – Comtypes

6) MiGeoDataSet = MiShape.QueryInterface(esriGeoDataset.IGeoDataset)
7) MiSpatialReference = MiGeoDataSet.SpatialReference
8) print "Datum: " , MiSpatialReference.Name


En ArcObject: si queremos saber el datum y proyección del shape, usamos esta sentencia que tiene una variable MiGeoDataSet de tipo IGeoDataset y luego creamos la variable MiSpatialReference de tipo ISpatialReference para guardar ese datum y proyección contenido en MiGeoDataSet.SpatialReference.


En Python: en la línea 7, tenemos la variable MiShape, que viene ser como un ILayer (proveniente de la PARTE F: MiShape = MiProyMxd.SelectedLayer), y queremos crear la variable MiGeoDataSet de tipo IGeoDataset, pero si buscamos en el “Object Browser” de ArcObjects no hay propiedad, método u objeto parecido a IGeoDataset en ILayer (ver en la imagen de abajo).

Para resolver este problema, usamos una vez más el QueryInterface que nos permite como en la PARTE A y F, crear la variable MiGeoDataSet como un IGeoDataset proveniente desde la variable MiShape que es un ILayer.

En la línea 8, creamos la variable MiSpatialReference que es de tipo ISpatialReference proveniente de la variable MiGeoDataSet, pero no usaremos el QueryInterface ya que IGeoDataset tiene un objeto de tipo SpatialReference como se muestra en el “Object Browser” de ArcObjects.


De forma grafica, vemos cual es la secuencia de lo explicado arriba:



Esperando que sea muy útil este post, nos vemos el próximo mes. Saludos
ª