Question

Create polygons from lists

  • 22 November 2018
  • 16 replies
  • 89 views

Badge

This seems as though it ought to be really simple but I can't figure out how to do it.

I have a number of text files which each define a polygon by listing the coordinates of its vertices. I'd like to build polygons from the coordinate lists.

Usually I'd use the VertexCreator to build points/polygons but I can't figure out how to get it to iterate through a list of variable length.

Some of the lists are also quite long (20+ coordinates) and it seems there should be an easier way than adding 20+ VertexCreators in series!


16 replies

Userlevel 4

Some vertex lists can be automatically parsed by FME, if they conform to some standard such as WKT.

Could you please post an example of such a coordinate list here?

Userlevel 2
Badge +17

The solution depends on how the coordinates are listed in a text. Could you please post a sample text that illustrates the format of coordinates list?

Badge

Some vertex lists can be automatically parsed by FME, if they conform to some standard such as WKT.

Could you please post an example of such a coordinate list here?

@david_r thanks for getting back to me. Here's a list:

PLY/1,54.3671208915,10.1422463646

PLY/2,54.3700921435,10.1422584956

PLY/3,54.3700947237,10.1537959675

PLY/4,54.3693817722,10.1537945973

PLY/5,54.3693818426,10.1541095477

PLY/6,54.3688144656,10.1541084576

PLY/7,54.3688143977,10.1537935091

PLY/8,54.3671234716,10.1537838354

This is actually part of the metadata of a BSB file. The BSB reader in FME doesn't seem to pull in much of the file as attributes so I'm reading it in as a text file to access this data and using a stringsearcher to identify these coordinate pairs. So, this is part of a file with lots of different information in it.

Badge

The solution depends on how the coordinates are listed in a text. Could you please post a sample text that illustrates the format of coordinates list?

@takashi I've posted a sample below from one of the files. It's part of a BSB header so contains all the standard BSB header info but the BSB reader doesn't read this data in. Therefore I'm reading it in as a text file. I'm trying to extract the PLY polygons.

Userlevel 4

@david_r thanks for getting back to me. Here's a list:

PLY/1,54.3671208915,10.1422463646

PLY/2,54.3700921435,10.1422584956

PLY/3,54.3700947237,10.1537959675

PLY/4,54.3693817722,10.1537945973

PLY/5,54.3693818426,10.1541095477

PLY/6,54.3688144656,10.1541084576

PLY/7,54.3688143977,10.1537935091

PLY/8,54.3671234716,10.1537838354

This is actually part of the metadata of a BSB file. The BSB reader in FME doesn't seem to pull in much of the file as attributes so I'm reading it in as a text file to access this data and using a stringsearcher to identify these coordinate pairs. So, this is part of a file with lots of different information in it.

We'll need a bit more context, how is the list represented in your workspace?

Badge +3

@erinjayaustin

if they are ordered would a LineBuilder not suffice? Group by/connection break attribute = fme_feature_type

(make sure last coord = first coord of course) If they close they become polygons.

 

Badge

We'll need a bit more context, how is the list represented in your workspace?

I'm using 2 string searchers to extract the x-coords and y-coords, which creates a list of the subexpression matches. I've attached an image of what the 2 lists look like for this feature.

Badge

I'm using 2 string searchers to extract the x-coords and y-coords, which creates a list of the subexpression matches. I've attached an image of what the 2 lists look like for this feature.

I'd like to plot the points (x-coord{n}.part,y-coord{n}.part).

Userlevel 4

Here's a possible solution using the PythonCaller:

def GeneratePolygon(feature):
    x_coords = [float(x) for x in feature.getAttribute('x-coord{}.part') or []]
    y_coords = [float(y) for y in feature.getAttribute('y-coord{}.part') or []]
    feature.addCoordinates(zip(x_coords, y_coords))

Add a LineCloser after the PythonCaller.

Badge

Here's a possible solution using the PythonCaller:

def GeneratePolygon(feature):
    x_coords = [float(x) for x in feature.getAttribute('x-coord{}.part') or []]
    y_coords = [float(y) for y in feature.getAttribute('y-coord{}.part') or []]
    feature.addCoordinates(zip(x_coords, y_coords))

Add a LineCloser after the PythonCaller.

Thanks. I'll give this a go and see how I get on. 

Userlevel 2
Badge +17

Other approaches.

You can convert the two lists to a single structured list consisting of two members (e.g. "_coord{}.x" and "_coord{}.y") with the AttributeManager (Rename Action), then explode the list (ListExploder), create vertices (VertexCreator), build a line (LineBuilder) and finally close the line to form a polygon (LineCloser).

Alternatively, the JSONTemplater with this template expression creates a GeoJSON object representing a polygon. You can replace it with a polygon geometry using the GeometryReplacer (Geometry Encoding: GeoJSON).

let $xs := fme:get-list-attribute('x-coord{}.part')
let $ys := fme:get-list-attribute('y-coord{}.part')
return
{
    "type" : "Polygon",
    "coordinates" : [
        [
            for $i in (1 to count($xs))
            return [xs:double($xs[$i]), xs:double($ys[$i])],
            [xs:double($xs[1]), xs:double($ys[1])]
        ]
    ]
}
Userlevel 2
Badge +17

Here's a possible solution using the PythonCaller:

def GeneratePolygon(feature):
    x_coords = [float(x) for x in feature.getAttribute('x-coord{}.part') or []]
    y_coords = [float(y) for y in feature.getAttribute('y-coord{}.part') or []]
    feature.addCoordinates(zip(x_coords, y_coords))

Add a LineCloser after the PythonCaller.

Be aware that the script would throw an error If the Python Compatibility was set to 3.x. with Python 3.x, you should modify the last line. e.g.

    feature.addCoordinates([(x, y) for x, y in zip(x_coords, y_coords)])
Badge

Other approaches.

You can convert the two lists to a single structured list consisting of two members (e.g. "_coord{}.x" and "_coord{}.y") with the AttributeManager (Rename Action), then explode the list (ListExploder), create vertices (VertexCreator), build a line (LineBuilder) and finally close the line to form a polygon (LineCloser).

Alternatively, the JSONTemplater with this template expression creates a GeoJSON object representing a polygon. You can replace it with a polygon geometry using the GeometryReplacer (Geometry Encoding: GeoJSON).

let $xs := fme:get-list-attribute('x-coord{}.part')
let $ys := fme:get-list-attribute('y-coord{}.part')
return
{
    "type" : "Polygon",
    "coordinates" : [
        [
            for $i in (1 to count($xs))
            return [xs:double($xs[$i]), xs:double($ys[$i])],
            [xs:double($xs[1]), xs:double($ys[1])]
        ]
    ]
}

Thank you very much @takashi, this worked for me!

Badge

Here's a possible solution using the PythonCaller:

def GeneratePolygon(feature):
    x_coords = [float(x) for x in feature.getAttribute('x-coord{}.part') or []]
    y_coords = [float(y) for y in feature.getAttribute('y-coord{}.part') or []]
    feature.addCoordinates(zip(x_coords, y_coords))

Add a LineCloser after the PythonCaller.

I couldn't get this to work, but I don't know Python very well! When I have a bit more time I'll have another go as I like to be able to understand everything in my workspaces! Thank you for the help.

Badge +14

Put together this transformer that makes a line, but I think making a polygon would be just as simple :) https://hub.safe.com/publishers/runneals/transformers/listlinebuilder

For anyone else trying to solve this, I had a similar issue where I wanted to create 40 simple rectangular polygons without using a reader. I came up with the following solution, which isn't particularly elegant, but it gets the job done! I hope I've covered all the parameters that matter.

 

Creator

  • Number to Create: 1

AttributeManager

  • Output attribute: coordinates
  • Output value: text with x0 y0,x1 y1,x2 y2,x3 y3, x4 y4, ... , x0 y0 on each line (first and last coordinate pairs are the same)

AttributeSplitter

  • Attribute to split: coordinates
  • Delimiter or Fomat String: [new line]
  • List Name: _coordinate_list

ListExploder

  • List Attribute: _coordinate_list{}
  • Element Index: _polygon_index

AttributeSplitter_2

  • Attribute t split: _coordinate_list
  • Delimiter or Format String: [comma]
  • List Name: _coord_pair

ListExploder_2

  • List Attribute: _coord_pair{}
  • Element Index: _polygon_coord_index

AttributeSplitter_3

  • Attribute to split: _coord_pair
  • Delimiter or Format String: [space]
  • List Name: _x_y

VertexCreator

  • Mode: Replace with Point
  • X Value: _x_y{0}
  • Y Value: _x_y{1}

LineBuilder

  • Group Processing: Ticked
  • Group By: _polygon_index

 

If the LineBuilder outputs a Line, your first and last coordinates aren't the same. Otherwise you should have polygons good to go.

Reply