Data QA: Identifying Invalid Geometry Types

Liz Sanderson
Liz Sanderson
  • Updated

Introduction

An Invalid Geometry Type is a geometry that doesn't match the feature it represents, such as a bicycle path stored as a point or a road stored as a polygon. An organization's rules can change the context as needed: for example, a municipal organization might store each street light as a polygon showing where its light reaches. On that layer, a street light stored as a point has the wrong type.

For our sample data, the City of Vancouver sets the rule by publishing its street projects layer as a shapefile with a PolyLine geometry type, the shapefile type for lines. Any feature that isn't a line has an invalid geometry type.

FME reads features like these without errors, but a format that supports only one geometry type cannot store them. FME can easily identify problematic features with a GeometryFilter by routing each chosen geometry type to a separate output port. You might then use a GeometryCoercer to convert them to a more appropriate geometry for your business use case. 

Once you know what the data holds, Data QA: Invalid Spatial Schemas shows how to set a permitted geometry type on the writer and log each feature that fails the check.

The GeometryValidator checks whether a geometry is structurally sound, for example, a line that crosses itself or a hole that lies outside its polygon. It does not check whether the geometry type suits the layer. For worked examples, see Data QA: Identifying Self-Intersections with FME and Invalid OGC Geometry Examples.

Source Data

The sample is city-project-package-street.geojson, a City of Vancouver feature class of 290 street project locations. Each feature is a road or utility project. Download it from the Files section.

FME records each feature's geometry type in fme_type, which, across the 290 features, takes four values:

  • 276 line features (fme_line): 118 LineString, 156 MultiLineString, and 2 geometry collections that hold only lines. These are the street segments the layer is for.
  • 8 area features (fme_area): 7 polygons and 1 multipolygon. Closure zones, laydown areas, and development sites. Each is an area on the ground, and none of them can stay on a layer the City declares linear.
  • 1 point (fme_point), a raised crossing at Quebec Street and 5th Avenue.
  • 5 geometry collections (fme_collection), each mixing lines and polygons in one feature.

In Data Preview, add fme_type and fme_geometry to the table with Columns..., then sort on fme_type to group the features by type. Data Preview hides fme_attributes until you add them. The fme_type reference covers every value and the fme_geometry each one pairs with.



The City's shapefile export (city-project-package-street.shp) holds the same 290 records: 274 with PolyLine geometry and 16 with none. FME reads the 16 with fme_type set to fme_no_geom. They are the 14 features that are not lines plus the 2 line-only collections, so the export dropped every geometry collection, whatever it held.

Step-by-Step Instructions

Part 1: Separating the Geometry Types

1. Add a GeoJSON Reader

In a blank workspace, select Build > Readers > Add Reader.

2. Add a GeometryFilter

Connect a GeometryFilter to the reader feature type. The transformer creates one output port per geometry type you select, and sends everything else to the <Unfiltered> output port.

  • Output Ports: Line, Area

Leave Point unselected. The single point then goes to the <Unfiltered> output port, where anything you haven't planned for arrives.

3. Run the Workspace

Run the workspace and read the record counts on the canvas.

  • Line output port: 281
  • Area output port: 13
  • <Unfiltered> output port: 1

The GeometryFilter has 295 records leaving a transformer that 290 entered. Five of those geometry collections hold lines and polygons together, and in its default Simple mode, the GeometryFilter splits each into one feature per geometry type. Five projects therefore appear on both the Line output port and the Area output port, each half carrying a full copy of the title, date, and URL. Nothing in the log reports the split, and the only symptom is output port counts that do not sum to the input. Check that sum on every GeometryFilter you build.

Inspect & sort the Area output port on fme_geometry to see it. 7 rows read fme_polygon, and 6 read fme_aggregate, because the GeometryFilter wraps each part it splits off in an aggregate. Five of those 6 are the split halves, named New St. Paul's Hospital, Chinatown Fire Dragon Festival, Arbutus Greenway, SE-2026-00585, and Thornton Pump Station Upgrades, and each also appears on the Line output port carrying the same attributes. The sixth is Green Infrastructure Bioretention Rehabilitation, which arrived as a genuine multipolygon rather than being split.

The two collections that hold only lines already read fme_line, and the GeometryFilter passes them to the Line output port whole.

Part 2: Counting What Does Not Belong

A QA report needs a count per problem type, not a single total. Thirteen areas and a point is a different conversation from two hundred.

1. Add an AttributeExposer

Connect an AttributeExposer to both the Area output port and the <Unfiltered> output port of the GeometryFilter.

  • Attributes to Expose:
  • Attribute: fme_type

fme_type already exists on every feature, which is why Data Preview can show it, but FME Workbench doesn't offer it in attribute pickers until you expose it. The AttributeExposer changes nothing about the features themselves. Select fme_type from the drop-down, which lists fme_ attributes that are not currently exposed.

2. Add an AttributeCreator

Connect an AttributeCreator downstream of the AttributeExposer and stamp each feature with the reason it was flagged.

  • Output Attribute: qa_issue
  • Value: @Value(fme_type) on a line layer

Add the words "on a line layer" immediately after the attribute reference so each label reads as a sentence, such as "fme_area on a line layer". You could use a similar approach with other attributes in your own workspaces to implement more informative debug messages.

3. Add a StatisticsCalculator

Connect a StatisticsCalculator to the AttributeCreator to produce one row per problem type.

  • Group Processing: enabled
  • Group By: qa_issue
  • Statistics to Calculate
    • Attribute: qa_issue
      • Total Count: selected

Total Count counts every feature in each group, so the output attribute qa_issue.total_count reports how many features carry each qa_issue value.

4. Run the Workspace

Read the Summary output port in Data Preview. Each row gives a problem type and how many features carry it.

  • fme_area on a line layer: 13
  • fme_point on a line layer: 1

Two rows, because all 13 features on the Area output port report fme_type as fme_area. The five halves that the GeometryFilter split off the collections report it too, so they are counted as areas alongside the 8 that arrived as areas.

Part 3: Coercing Areas and Writing the Lines

An area has a boundary and a boundary is a line, so an area converts without inventing any coordinates. A single point has no boundary to work with.

1. Add a GeometryCoercer

Connect a GeometryCoercer to the Area output port of the GeometryFilter.

  • Geometry Type: fme_line

2. Check the Converted Geometry

Run the workspace, then inspect the Coerced output port in Data Preview. A record count tells you a feature came through; it does not tell you what shape it now has.

Every area converts. All 13 features arrive on the Coerced output port, and the Untouched output port stays empty. Each one now reports fme_type as fme_line, but sort on fme_geometry as well, because the 13 are not all the same shape:

  • 7 read fme_geometry as fme_line. Hawks Ave Closure Pilot is one of them, a single closed line of 5 vertices, which is the polygon's ring with its closing vertex kept.
  • 6 read fme_geometry as fme_aggregate. Green Infrastructure Bioretention Rehabilitation is the multipolygon, and its 5 parts become 5 closed lines held in one feature. The other 5 are the halves of the split projects from Part 1.

fme_type reads fme_line on all 13, so a check against fme_type alone reports a clean conversion and tells you nothing else. fme_geometry is where the aggregates show. Read both when a downstream format needs single lines, because 6 of these are bundles of lines traveling as one feature.

3. Add a Writer

Select Build > Writers > Add Writer, then connect the Coerced output port of the GeometryCoercer and the Line output port of the GeometryFilter to the new writer feature type. This article calls it output data.

  • Format: Esri Shapefile
  • Dataset: a folder or .zip file of your choice

This article uses the Esri Shapefile for output because that is the format the City publishes the layer in. Use your own output format if you prefer, as long as it can be restricted to one geometry type per feature type, as GeoPackage can, and restrict the geometry with that writer's equivalent setting. A format that accepts mixed geometry, such as GeoJSON, will write everything without complaint, so it cannot tell you whether the conversion worked.

Open the output-data writer feature type and go to the User Attributes tab to restrict the geometry it accepts to lines.

  • Schema Definition: Dynamic
  • Geometry Definition
  • Geometry Type: shapefile_line

Geometry Type can only be changed when Schema Definition is Manual or Dynamic. Dynamic takes the attribute definitions from the data when the workspace runs, so the shapefile keeps every attribute. Shapefile field names are limited to 10 characters, so project_title is written as project_ti and expected_completion_date as expected_c.

Avoid Manual unless you list every attribute yourself. With Manual and an empty attribute table, the shapefile is written with geometry and no attributes, and the run still reports success.

All 294 features that reach this feature type are written: the 281 from the Line output port and the 13 coerced areas. None are rejected, because everything arriving is now a line.

4. Route the Point Out for Review

Add a second writer feature type named review and connect the <Unfiltered> output port to it, so the raised crossing at Quebec Street and 5th Avenue leaves the workflow as a named output.

The point skips the GeometryCoercer because coercing it changes nothing: the GeometryCoercer leaves any feature with only one coordinate unchanged when asked for fme_line. Someone has to decide whether to drop the crossing, buffer it, or send it back to the City, and a QA workflow's job is to put that decision in front of them rather than make it silently.

Applying This to Other Data

The finished workspace reads 290 features, writes 294 lines to a shapefile that accepts only lines, and sends 1 point to review. The count goes up because 5 projects now appear twice, once for their line part and once for their converted area part, and the tip in Part 3 step 4 covers recombining them.

The same checks carry over to any layer headed for a destination that holds one geometry type:

  • Find out what the destination accepts before you start. A schema, a format's documentation, or the publisher's own export will tell you. Here, it came from the City's shapefile.
  • After every GeometryFilter, check that the output port counts add up to the input count. A total above the input means collections were split.
  • After coercion, read fme_geometry and fme_type. fme_type confirms the type changed, and fme_geometry shows whether you have single lines or bundles.
  • Send anything that cannot be converted to its own output for review rather than letting the writer drop it.

Additional Resources

Data Attribution

The data used here originates from open data made available by the City of Vancouver, British Columbia. It contains information licensed under the Open Government License - Vancouver.

Was this article helpful?

We're sorry to hear that.

Please tell us why.

As of January 14th, 2026, comments on knowledge base articles have been closed. To make sure questions don’t get missed and to enable more community support, we’ve moved discussions to the FME Community. If you have a question or a comment about this article, please create a new post or create a support ticket.