Short answer

A geological map in QGIS is built on three styling decisions: how lithological units are coloured (Categorized symbology on a stable code field), how structural data is symbolised (rotated, data-defined point markers for strike/dip, line symbols for contacts and faults), and how the whole thing stays consistent across maps (saved .qml/.sld styles or a shared layer_styles table). Style on codes, not names; rotate symbols with data-defined expressions; and never re-pick colours by hand twice.

Colouring lithological units

The instinct is to use Categorized symbology on the unit name. Resist it. Names get edited, translated, and abbreviated, and the moment one changes, the symbol resets to a random colour. Instead key the symbology on a stable code field — a map-unit symbol like Qal, Kjm, or a numeric unit_id. The display label can still show the full name.

In Layer Properties ▸ Symbology ▸ Categorized:

  1. Set Value to your code field (or an expression if codes are composite).
  2. Click Classify to enumerate the categories.
  3. Assign colours deliberately. For published geology, follow an established palette — many national surveys publish RGB/CMYK values, and the USGS/FGDC Digital Cartographic Standard for Geologic Map Symbolization defines fills and patterns. Geological time-unit colours follow the CGMW/ICS commission chart (e.g. Cretaceous greens, Jurassic blues).

To make this repeatable, export the assignment once via the Color Ramp ▸ Save Color Table, or better, save the entire symbology as a style (below) so age-to-colour mapping is locked. Where lithology needs a hatched fill rather than a flat colour (limestone bricks, sandstone dots), use a line pattern fill or point pattern fill symbol layer, or import an SVG/raster fill — the FGDC patterns are available as fill libraries.

Rule-based symbology for contacts, faults and scale

Categorized covers one field. Real geological linework needs combinations, and that is what Rule-based symbology is for. A single contacts/faults layer can carry feature_type (contact, fault, thrust) and certainty (observed, inferred, concealed), and you want all of these rendered correctly and to change with map scale.

Example rules on a structural-lines layer:

  • "feature_type" = 'fault' AND "certainty" = 'observed' → solid 0.6 mm red line.
  • "feature_type" = 'fault' AND "certainty" = 'inferred' → dashed red line.
  • "feature_type" = 'thrust' → red line with a Marker line sub-symbol placing triangle teeth on the hanging-wall side at a fixed interval.
  • "feature_type" = 'contact' AND "certainty" = 'concealed' → dotted gray line.

Each rule can also carry a scale range (minimum/maximum 1:n) so minor contacts disappear at small scales and reappear when zoomed in. This is far cleaner than maintaining separate layers per feature type.

Structural point data: strike and dip

Strike-and-dip readings are the canonical "GIS can't do that" complaint — but QGIS handles them well with data-defined properties.

Store measurements as points with at least dip_dir (dip direction, 0–360 azimuth) and dip (dip amount, degrees). Then:

  1. Apply an SVG marker or simple marker shaped like a strike-dip tick (a line with a short perpendicular tick on the down-dip side). Several geology SVG sets exist; you can also build the glyph from two marker sub-layers.
  2. In the marker's Rotation field, click the data-defined override (the small dropdown) and set it to the expression "dip_dir" — or "dip_dir" - 90 depending on how your glyph is drawn relative to strike. The symbol now rotates per feature.
  3. Add a label using "dip" as the text, with a data-defined placement offset so the number sits beside the tick, not on it. A Quadrant offset keyed to the dip direction keeps labels off the line.

For horizontal beds (dip = 0) and vertical beds (dip = 90), the standard convention uses distinct glyphs; a rule-based override on "dip" = 0 and "dip" = 90 swaps the marker.

Worked example: a reusable lithology style

Say you map the same Quaternary-to-Cretaceous sequence across several survey areas. Once:

  • Build the Categorized symbology on unit_code with FGDC-aligned colours and fills.
  • Layer Properties ▸ Symbology ▸ Style (bottom-left button) ▸ Save Style ▸ As QGIS QML Style Filelithology.qml.

In every new project, Load Style ▸ from file applies the same colours instantly, provided the field name matches. For team-wide consistency, store styles in the GeoPackage itself (Save Style ▸ Save in database), or in PostGIS where QGIS reads and writes the public.layer_styles table — every analyst opening that layer gets the default style automatically. Use .sld instead of .qml only when the style must also drive GeoServer/WMS, accepting that SLD does not support every QGIS symbol feature (some pattern fills and blend modes are lost).

Common pitfalls and why they happen

  • Colours scramble after an edit. You styled on a name/label field that changed. Style on a code field.
  • Strike-dip symbols all point the same way. The rotation field was set as a static value, not a data-defined expression. Use the override on dip_dir.
  • .sld looks wrong in GeoServer but fine in QGIS. SLD cannot express QGIS-specific symbol layers (e.g. marker lines, some pattern fills). Simplify the symbol or keep a separate .qml for desktop.
  • Hatched fills disappear when printed. Pattern fills defined in pixels rescale unpredictably; define fill spacing and line widths in millimetres or map units, not pixels, so they survive export at any DPI.
  • Labels collide on dense maps. Enable label collision/priority and use rule-based scale visibility so minor units only label when zoomed in.

Quality checks before delivery

  • Verify every unit has an assigned symbol — categorized rendering silently drops values not in the list unless you add a fallback rule (the all other values / ELSE rule).
  • Cross-check the colour-to-age mapping against the published chart you committed to (ICS/CGMW or the client's standard).
  • Render at the target scale and DPI; confirm fills, dashes and tooth spacing read correctly on the exported PDF, not just on screen.
  • Confirm structural symbol rotation against a few known field readings.

Bathyl perspective

For geological cartography we treat the style file as a versioned asset, the same as the data. A lithology .qml checked into the project repository, plus styles stored in the delivery GeoPackage, means a map can be regenerated identically a year later and every sheet in a series shares one legend. Styling consistency is what lets a reader compare two maps without re-learning the colours.

Related reading

Sources