Checking geometries

Traduzco un post publicado por Joaquín del Cerro hace unos días que puede ser interesante:Hello everyone back again,It was some time ago I didn’t write this blog but I have been really busy working on new gvSIG developments. Anyway the other day, somebody said me they needed a new tool for check geometry problems in a table before load it into a posgreSQL database. They told me….

“It would be nice if we could have a tool that processes geometries in a layer, list all the problems and it allows clicking the geometry conflict and, better yet, locating the geometry conflict”

Indeed, gvSIG does not have anything near it but straight away I thought that, with a bit of luck, I could get a similar tool which they need.

But the script isn’t as simple. It cuts across a whole range of variables although it is not too big and it can help to design more complex tools.

The principle is simple: we analyze the features of the layer, select geometry and we invoke the method: “getValidationStatus”. This method is designed to validate geometry and returns information about validation process: identify problems and if it is possible where they are. Thus, we are building up a little HTML validation report.

An entry is generated for each incident with the following information:

  • Line number where the incident is located
  • Dump data with feature for line
  • Description of the problem
  • Link to the incident is located. Instead of add an URL, we will add the WKT point for the incident layer.
wktgeom = vs.getProblemLocation().convertToWKT()
output.write('<a href="%s">%5.5d</a> %s<br>%s' % (wktgeom,i, feature.toString(),vs.getMessage()))

Once we have been analysed all the features we will have to generate the report. To do so, we will display a window with HTML text using “showTexDialog” function from “ApplicationManager”.

application = ApplicationLocator.getManager()
application.showTextDialog(
  WindowManager.MODE.TOOL,
  "Problems in layer "+layer.getName(),
  informe.getvalue()
)

It would be enough if we simply want to generate the report but it would be very nice if the incident area is displayed on the screen by clicking the incident in the report. To do this, we will have a bit more work to do. “showTextDialog” function allow us to pass a “listener” as optional parameter to receive events coming from HTML report. To do so, we will have to create a class to inherit from “HyperlinkListener”.

class MyHyperlinkListener(HyperlinkListener):
  def __init__(self,vista):
    self.vista = vista
    
  def hyperlinkUpdate(self, h):
    if str(h.getEventType()) == "ACTIVATED":
      wktgeom = h.getDescription()
      print wktgeom
      manager = GeometryLocator.getGeometryManager()
      point = manager.createFrom(wktgeom)
      centerViewinPoint(self.vista,point)

Our class will have in the constructor, a view to work with and we add the code to execute in “hyperlinkUpdate” method. The code will be executed every time while interact with link of our report. Either because we mouse over the link or because of we click on the link. We will insert the code in this method, verifying that click the link is filtered. Then, we will receive “ACTIVATED” event type. We will use the associated point for the hyperlink and rebuild our point according we had added to WKT point.

manager = GeometryLocator.getGeometryManager()
point = manager.createFrom(wktgeom)

And then you pan the view to the point

centerViewinPoint(self.vista,point)

There are a few more things to comment…

Sometimes, it will be run on the big layers and it would be nice if it included progress reporting. To do so, we will use “taskStatus” variable. It is associated with each script to report progress of the process. First of all, we associate it to the application status bar to see the progress of the process. That’s as simple as invoking the “add” method of the variable.

Then we will use the “setRangeOfValues” method to indicate the iterations necessary for our process. With this method, we will indicate that iterations go from 0 to number of records included in the layer to process.For each iteration, we will invoke “setCurValue” method to keep the process located. After our features have been reviewed we will invoke “terminate” method to get rid of “taskStatus” variable and indicate that the process is completed. Finally, we will use “remove” method to delete the variable from gvSIG status bar.

try:
  # Adding a progress bar to the gvSIG status bar
  taskStatus.add()
  # Indicate the number of iterations
  taskStatus.setRangeOfValues(0,layer.features().getCount())
  for feature in layer.features():
    # For each iteration there is a report process in the progress bar indicating where is located
    taskStatus.setCurValue(i)
    ...
    i+=1
finally:  
  # Finally
  # For indicate to the status bar the process is completed
  taskStatus.terminate()
  # To delete from gvSIG status bar
  taskStatus.remove()

Well, it still lacks many details like catching errors, StringIO use to create reports or generate HTML code, but I believe that people who know something about gvSIG scripting and python programming could follow it. At the end of this blog you will find the full code.

I also have a problem with “certerViewinPoint” method. There is some calculation error and we can’t correctly pan the view. That is a minor detail that you can improve if you work with the script.

I hope it will serve you as gvSIG tool and example of new types of scriptings.

To test the script you need a layer with damage or non valid geometries. I have any missed layer, if I located it, I upload the layer and I give you the link to test.

The script just run with gvSIG desktop 2.1.0 build 2231 or higher 😉

Greetings everyone

Joaquin

from gvsig import *
from org.gvsig.tools.swing.api.windowmanager import WindowManager
import StringIO
from commonsdialog import *
from javax.swing.event import HyperlinkListener
from org.gvsig.fmap.geom import GeometryLocator
from org.gvsig.fmap.geom import Geometry

def centerViewinPoint(view,center):
    env = view.getMap().getViewPort().getEnvelope();
    movX = center.getX()-env.getCenter(0);
    movY = center.getY()-env.getCenter(1);
    minx = env.getMinimum(0) + movX;
    miny = env.getMinimum(1) + movY;
    maxX = env.getMaximum(0) + movX;
    maxY = env.getMaximum(1) + movY;
    env = GeometryLocator.getGeometryManager().createEnvelope(
          minx, miny,
          maxX, maxY,
          Geometry.SUBTYPES.GEOM2D);
    view.getMap().getViewPort().setEnvelope(env);
    view.getMap().invalidate()


class MyHyperlinkListener(HyperlinkListener):
  def __init__(self,vista):
    self.vista = vista
    
  def hyperlinkUpdate(self, h):
    if str(h.getEventType()) == "ACTIVATED":
      # Just process “ACTIVATED” events which are the clicks on the links
      #  Collect the geometry we have used in WKT link.
      wktgeom = h.getDescription()
      print wktgeom
      # Collect Rebuild the point from WKT
      manager = GeometryLocator.getGeometryManager()
      point = manager.createFrom(wktgeom)
      # Once we have that, we pan the view to the point centerViewinPoint(self.vista,point)


def main(*args):
  layer = currentLayer()
  if layer == None:
    msgbox("Select layer to validate")
    return
  informe = StringIO.StringIO()  
  informe.write("<ul>\n")
  i=0
  hay_problemas = False
  try:
    # Add progress bar to gvSIG status bar.
    taskStatus.add()
    #  Indicate number of iterations that are going to be carried out.
    for feature in layer.features():
      # For each iteration, we will report to the progress bar to keep the process located
      taskStatus.setCurValue(i)
      vs = feature.geometry().getValidationStatus();
      if not vs.isValid() :
        hay_problemas = True
        if vs.getProblemLocation()!=None:
          wktgeom = vs.getProblemLocation().convertToWKT()
        else:
          try:
            # Catch the errors to avoid failure in calculating centroid
            # because of damage geometry.
            wktgeom = feature.geometry().centroid().convertToWKT()
          except:
            wktgeom = None
        informe.write("<li>")
        if wktgeom == None:
          informe.write('%5.5d %s<br>%s' % (i, feature.toString(),vs.getMessage()))
        else:
          informe.write('<a href="%s">%5.5d</a> %s<br>%s' % (wktgeom,i, feature.toString(),vs.getMessage()))
        informe.write("</li>")
      i+=1
  finally:  
    # Finally
    # Indicate the process is finished to the progress bar
    taskStatus.terminate()
    taskStatus.terminate()
    # And eliminate it from the gvSIG status bar.
    taskStatus.remove()
    
  informe.write("</ul>\n")
  if hay_problemas :
    application = ApplicationLocator.getManager()
    application.showTextDialog(
      WindowManager.MODE.TOOL, # TOOL or WINDOW  depending on whether we are interested.
      "Problems in layer "+layer.getName(),
      informe.getvalue(),
      MyHyperlinkListener(currentView())
    )
  else:
    msgbox("Layer "+layer.getName()+" is valid.")
This entry was posted in development, english, gvSIG Desktop, scripting. Bookmark the permalink.

Leave a comment