Brincando com ArcObjects #2
No último post sobre ArcObjects, falamos como construir pontos de uma maneira simples. Hoje vamos brincar com alguns polígonos e polilinhas. Qual é a diferença? Bem, como explicado anteriormente, um polígono é uma coleção de anéis (rings), formados por pontos ou por segmentos. As polilinhas funcionam quase da mesma maneira, só que ao invés de anéis, as polilinhas são formadas por uma coleção de caminhos (paths).
Não podemos criar estas geometrias de alto nível, sem criar antes as geometrias de baixo nível que as compõe. É a maneira na qual o ArcGIS trabalha, e é a mais sã, do ponto de vista de desenvolvimento.
Primeiro vamos construir um polígono. Podemos construir os anéis separadamente, de pontos ou de segmentos e logo depois, adicionar à uma coleção de geometrias, na qual o próprio ArcGIS irá entender como um polígono.
public static IPolygon ConstruirPolígono()
{
object _missing = Type.Missing;
IGeometryCollection poligonoFinal = new PolygonClass();
// vamos criar primeiro nosso anel exterior
// perceba com estamos utilizando uma interface IPointCollection
IPointCollection anelExterior = new RingClass();
// lembram da função BuildPoint do post anterior?
anelExterior.AddPoint(BuildPoint(0,0,0),ref _missing, ref _missing);
anelExterior.AddPoint(BuildPoint(0,1,0),ref _missing, ref _missing);
anelExterior.AddPoint(BuildPoint(1,1,0),ref _missing, ref _missing);
anelExterior.AddPoint(BuildPoint(1,0,0),ref _missing, ref _missing);
// o último ponto deve ser sempre igual ao primeiro - somente assim
// o polígono está fechado
// podemos utilizar também uma outra técnica para usar o primeiro ponto
// sem duplicar o ponto inicial - muito útil se você não sabe de antemão qual é o primeiro ponto.
// anelExterior.AddPoint(anelExterior.get_Point(0),ref _missing, ref _missing);
anelExterior.AddPoint(BuildPoint(0,0,0));
poligono.AddGeometry(anelExterior as IGeometry, ref _missing, ref _missing);
// definir a referência espacial - sem a referência espacial o ArcGis não realiza operações espaciais direitinho!
return poligono as IPolygon;
}
Este é um código simples para a criação de um polígono igualmente simples. Vamos aumentar um pouco a dificuldade inserindo outros anéis.
public static IPolygon ConstruirPolígono()
{
object _missing = Type.Missing;
IGeometryCollection poligonoFinal = new PolygonClass();
// vamos criar primeiro nosso anel exterior
// perceba com estamos utilizando uma interface IPointCollection
IPointCollection anelExterior = new RingClass();
// lembram da função BuildPoint do post anterior?
anelExterior.AddPoint(BuildPoint(0,0,0),ref _missing, ref _missing);
anelExterior.AddPoint(BuildPoint(0,10,0),ref _missing, ref _missing);
anelExterior.AddPoint(BuildPoint(10,10,0),ref _missing, ref _missing);
anelExterior.AddPoint(BuildPoint(10,0,0),ref _missing, ref _missing);
// o último ponto deve ser sempre igual ao primeiro - somente assim
// o polígono está fechado
// podemos utilizar também uma outra técnica para usar o primeiro ponto
// sem duplicar o ponto inicial - muito útil se você não sabe de antemão qual é o primeiro ponto.
anelExterior.AddPoint(anelExterior.get_Point(0),ref _missing, ref _missing);
IPointCollection anelInterior = new RingClass();
anelInterior.AddPoint(BuildPoint(1,1,0), ref _missing, ref _missing);
anelInterior.AddPoint(BuildPoint(1,2,0), ref _missing, ref _missing);
anelInterior.AddPoint(BuildPoint(2,2,0), ref _missing, ref _missing);
anelInterior.AddPoint(BuildPoint(2,1,0), ref _missing, ref _missing);
anelInterior.AddPoint(anelInterior.get_Point(0),ref _missing, ref _missing);
// nao é permitido sobrepor anéis, à menos que eles tenham orientações contrárias
// nosso anel exterior tem orientação horária - todos os anéis exteriores devem ter orientação horária!
// anéis interiores, que descrevem buracos em nossos polígonos devem ter orientação anti-horária!
ICurve curva = anelInterior as ICurve;
curva.ReverseOrientation();
poligono.AddGeometry(anelExterior as IGeometry, ref _missing, ref _missing);
poligono.AddGeometry(anelInterior as IGeometry, ref _missing, ref _missing);
// definir a referência espacial - sem a referência espacial o ArcGis não realiza operações espaciais direitinho!
return poligono as IPolygon;
}
Bem, é fácil trabalhar com geometrias da ESRI. Claro, quanto mais complexa a geometria, mais complexo deverá ser o código para cuidar da mesma. Mas não é nenhum bixo de 7 cabeças. Um outro detalhe interessante é que revertemos a orientação da geometria interna usando uma outra interface, e adicionamos nosso anel original. Por que funciona? A biblioteca ArcObjects fica responsável por notificar a geometria original de que certa operação ocorreu e a realiza de acordo.
Em geral, os passos para se trabalhar com geometrias complexas é construir anéis ou segmentos, criados através da interface IPointCollection, pois assim podemos ir adicionando os pontos que temos interesse e depois ajeitar a orientação de cada anel para nos dar o resultado desejado.
Certo, mas e um polígono que contenha curvas? O formato shapefile não suporta(va) o desenho delas e estão disponíveis à partir dos geodatabases (as curvas são convertidas em diversos segmentos, aproximando o resultado. Mas podemos criar um polígon com curvas via código? Sim, claro! O segredo, neste caso, é separar cada segmento em um anel, mesmo que ele, no final, seja parte de uma coisa só.
Vamos criar dois anéis, um para nosso segmento curvo e o outro para nossos segmentos retos. Juntaremos os dois em apenas um anel e depois adicionaremos este anel ao polígono.
Para construir segmentos curvos, existem diversas interfaces do tipo IConstruct* (IConstructCircularArc, IConstructBezierCurve, etc.), cada uma com suas particularidades.
object _missing = Type.Missing;
IGeometryCollection polygon = new PolygonClass();
// building circular arc
IPoint p1 = PointPolygonBuilder.BuildPoint(1, 2);
IPoint p2 = PointPolygonBuilder.BuildPoint(1, 1);
IPoint p3 = PointPolygonBuilder.BuildPoint(1, 0);
IPoint p4 = PointPolygonBuilder.BuildPoint(0, 0);
IPoint p5 = PointPolygonBuilder.BuildPoint(0, 1);
IConstructCircularArc constructCircularArc = new CircularArcClass();
constructCircularArc.ConstructThreePoints(p5, p2, p1, true);
ICircularArc circularArc = constructCircularArc as ICircularArc;
// end circular Arc
ISegmentCollection finalRing = new RingClass();
ISegmentCollection ring1 = new RingClass();
IPointCollection ring2 = new RingClass();
ring1.AddSegment(circularArc as ISegment, ref _missing, ref _missing);
ring2.AddPoint(p1, ref _missing, ref _missing);
ring2.AddPoint(p3, ref _missing, ref _missing);
ring2.AddPoint(p4, ref _missing, ref _missing);
ring2.AddPoint(p5, ref _missing, ref _missing);
finalRing.AddSegmentCollection(ring1);
finalRing.AddSegmentCollection(ring2 as ISegmentCollection);
polygon.AddGeometry(finalRing as IGeometry, ref _missing, ref _missing);
Note que deu um trabalhinho à mais para montar este segmento curvo. É bom deixar claro também, que o ponto (1,1) representado na figura, não entra no anel feito de segmentos retos, ele é apenas utilizado para construir o segmento curvo.
Outra coisa legal é ver que para acessar alguns métodos, você tem que instanciar certas interfaces. Notem que o finalRing e ring1 possuem propriedades semelhantes, pois têm a mesma interface. Já ring2 possui outros membros, pois é de interface diferente. Classes iguais, interfaces diferentes = propriedades diferentes.
E aí, o que acharam?
Abraços pessoal!
Related posts:


