Your country

Tools that support it use your country for local currency, number formats, units and paper size. Your choice is saved only in this browser.

Type a name or a two-letter code. Use the up and down arrow keys to move through the countries, Enter to choose one and Escape to close.

GIS & Geospatial Data (QGIS, GeoPandas, PostGIS)  Module 1 – Foundations: spatial questions and data models

Vector and raster data models

How GIS stores the world as features with attributes or as grids of cells, what cell size does to a measured area, and why map scale limits what data decides.

  • Beginner
  • 25 minutes
  • Examples run with Python 3.14.8 and Pyodide 314.0.7
  • By MySmartCoPilot
Free lesson, Pro answers: 25 minutes
Free: the whole lesson. With a pass: the model answers to its 2 interview questions.

What you will learn

  • Describe a vector feature as a geometry with attributes, and a raster as a grid with a cell size, bands and a nodata value
  • Decide whether a phenomenon is better stored as vectors or rasters, and as classes or measurements
  • Calculate an area from cell counts and cell size, and predict how coarser cells change it
  • Explain why a boundary compiled at 1:50,000 should not decide which side of it a house stands

Before you start

What you’ll learn · Before you start

What you will learn

  • Describe a vector feature as a geometry with attributes, and a raster as a grid with a cell size, bands and a nodata value
  • Decide whether a phenomenon is better stored as vectors or rasters, and as classes or measurements
  • Calculate an area from cell counts and cell size, and predict how coarser cells change it
  • Explain why a boundary compiled at 1:50,000 should not decide which side of it a house stands

Before you start

On this page

A lake drawn as a polygon covers 69.39 ha. Stored as a grid of 100 m cells, the same lake covers 68.00 ha, and the narrow inlet on its east side has disappeared. Both numbers are honest: they come from two different ways of storing the world, and each way decides what you can measure.

A GIS stores everything either as vector features, shapes that each carry a row of facts, or as rasters, grids of cells that each hold a value. Below you take each model apart, choose between them for the thing you want to store, compute an area from a raster and see what the cell size does to it, and judge whether data made at a given map scale can decide the question in front of you.

A vector feature is a geometry with a row of attributes

A feature: One thing in a vector layer, such as a ward, a road or a clinic: a geometry stored together with a row of attributes. is one thing in a layer: a ward, a road, a clinic. The QGIS introduction puts it in two parts: “A vector feature has its shape represented using geometry”, and vector features also have attribute: A value stored with a feature that is not part of its shape, such as a name or a count. The attributes of a layer's features form its attribute table., “text or numerical information that describe the features” (QGIS, Vector Data). A layer is therefore a table whose rows each hold a shape.

The geometry comes in three basic types. A point is one position. A line is a list of two or more positions. A polygon is a ring of positions whose last one repeats the first, so it closes around an area, and it may hold further rings inside it that cut holes. Each type has a multi-part form, such as a multipolygon for a ward split by a river. Step through one polygon with a hole, Lake Park, from its row in the layer to its text:

Interactive · step by step The parts of one vector feature Step through Lake Park, a park with a pond in it, from its row in a layer to the line of text that stores its shape.
The parts of one vector feature, step 7 of 7. Showing Feature: Lake Park, Geometry: Polygon (area 44 ha), Attributes, Outer ring (anticlockwise, 48 ha), Coordinate system, Inner ring: a pond (clockwise, minus 4 ha). In focus: Geometry: Polygon.Feature: Lake Parkone row of a parks layerGeometry: Polygonarea 44 haAttributesname, kindOuter ringanticlockwise, 48 haCoordinate systemmetres on the town gridInner ring: a pondclockwise, minus 4 haPOLYGON ((4400 4300, …), (4700 4500, …))

The drawing shows the whole scene; its 7 steps are listed below.

Every step as text
  1. One feature. Lake Park is one feature of a layer of parks, one row of its table. The row holds the shape of the park and the facts about it, side by side.
  2. Its geometry. The shape is a geometry of type Polygon. A point is one x y pair, a line is a list of pairs, and a polygon is one list of pairs for each of its rings.
  3. The outer ring. The park's outline runs through 5 pairs, from (4400 4300) round to (4400 4300) again, because a ring closes on its first point. It runs anticlockwise, which GeoJSON's standard asks of an outer ring.
  4. A hole. A second ring inside the first is a hole, here a pond of 200 m by 200 m. It runs clockwise, so its signed area is negative and the program subtracts it, 48 ha minus 4 ha.
  5. The attributes. The attributes are the row's other columns, here a name, Lake Park, and a kind, park. Every feature of a layer has the same columns, so the layer is a table with a shape in each row.
  6. What the numbers mean. The coordinates are metres on the town's grid only because the layer says so. That statement is the layer's coordinate reference system, shared by all its features; without it the pairs are just numbers.
  7. The shape as one line of text. Well-known text writes the whole geometry on one line, POLYGON with both rings in brackets, outer ring first. PostGIS, Shapely and QGIS all read and write it.

The last step showed well-known text (WKT): A plain-text form of a geometry from the OGC Simple Features standard, such as POINT (4000 5000). The same name is also used for a text format of coordinate reference systems., the plain-text form of a geometry from the OGC’s Simple Feature Access standard (OGC), which the PostGIS manual uses for its examples, such as POINT(0 0) and LINESTRING(0 0,1 1,1 2) (PostGIS manual). The program below holds four features of the town, measures each and writes it as text:

Four features: a point, a line and two polygons Python · vector/features.py
"""Four vector features: each one a geometry (a point, a line or the rings of a polygon) plus a row of attributes."""
import math

FEATURES = [
    {"type": "Point", "coordinates": (4_000, 5_000), "attributes": {"name": "C2", "kind": "clinic", "beds": 12}},
    {"type": "LineString", "coordinates": [(3_500, 6_000), (3_500, 5_000), (3_500, 2_800), (3_500, 0)],
     "attributes": {"name": "Station Road", "lanes": 2}},
    {"type": "Polygon", "coordinates": [[(2_000, 4_000), (4_000, 4_000), (4_000, 6_000), (2_000, 6_000), (2_000, 4_000)]],
     "attributes": {"name": "W02", "kind": "ward"}},
    {"type": "Polygon", "coordinates": [
        [(4_400, 4_300), (5_200, 4_300), (5_200, 4_900), (4_400, 4_900), (4_400, 4_300)],  # the park's outline
        [(4_700, 4_500), (4_700, 4_700), (4_900, 4_700), (4_900, 4_500), (4_700, 4_500)],  # a pond: a hole
    ], "attributes": {"name": "Lake Park", "kind": "park"}},
]


def signed_area(ring):
    """Shoelace formula: positive for an anticlockwise ring, negative for a clockwise one."""
    return sum(x1 * y2 - x2 * y1 for (x1, y1), (x2, y2) in zip(ring, ring[1:])) / 2


def length(points):
    return sum(math.dist(a, b) for a, b in zip(points, points[1:]))


def wkt(feature):
    """Well-known text, the plain-text form of a geometry in the OGC Simple Features standard."""
    pairs = lambda points: ", ".join(f"{x} {y}" for x, y in points)
    kind, coords = feature["type"], feature["coordinates"]
    if kind == "Point":
        return f"POINT ({coords[0]} {coords[1]})"
    if kind == "LineString":
        return f"LINESTRING ({pairs(coords)})"
    return "POLYGON (" + ", ".join(f"({pairs(ring)})" for ring in coords) + ")"


for f in FEATURES:
    kind, coords = f["type"], f["coordinates"]
    if kind == "LineString":
        measure = f"length {length(coords) / 1_000:.2f} km"
    elif kind == "Polygon":
        area = sum(signed_area(ring) for ring in coords)  # a hole is clockwise, so its area is taken away
        measure = f"area {area / 10_000:.0f} ha, {len(coords) - 1} hole(s)"
    else:
        measure = "no length or area"
    print(f"{f['attributes']['name']}: {kind}, {measure}, attributes {f['attributes']}")
    print(f"  {wkt(f)}")

Output

C2: Point, no length or area, attributes {'name': 'C2', 'kind': 'clinic', 'beds': 12}
  POINT (4000 5000)
Station Road: LineString, length 6.00 km, attributes {'name': 'Station Road', 'lanes': 2}
  LINESTRING (3500 6000, 3500 5000, 3500 2800, 3500 0)
W02: Polygon, area 400 ha, 0 hole(s), attributes {'name': 'W02', 'kind': 'ward'}
  POLYGON ((2000 4000, 4000 4000, 4000 6000, 2000 6000, 2000 4000))
Lake Park: Polygon, area 44 ha, 1 hole(s), attributes {'name': 'Lake Park', 'kind': 'park'}
  POLYGON ((4400 4300, 5200 4300, 5200 4900, 4400 4900, 4400 4300), (4700 4500, 4700 4700, 4900 4700, 4900 4500, 4700 4500))

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 features.py

A point has no length or area; Station Road is 6.00 km long; ward W02 covers 400 ha, and Lake Park 44 ha, because its pond’s ring runs clockwise and its signed area is taken away. GeoJSON’s standard requires exactly that orientation, anticlockwise outer rings and clockwise holes (RFC 7946, section 3.1.6), but it also asks readers not to reject polygons wound the other way, so code that reads other people’s files should subtract each hole’s area instead of trusting its sign.

Features that touch, such as two wards with a common edge, each store that edge’s positions. When the two copies do not match exactly, thin gaps or overlaps called slivers appear along the border, one of the errors of digitised data that the QGIS introduction describes. Keeping shared boundaries identical is called keeping the data’s topology clean.

A raster is a grid of cells tied to the ground by six numbers

A raster: A grid of equal cells, each holding one value per band, placed on the ground by a geotransform and a coordinate reference system. It suits imagery and quantities with a value everywhere. is “a grid of regularly sized pixels” whose fixed size sets its spatial resolution (QGIS, Raster Data). The QGIS introduction gives examples from 50 cm aerial photographs to 500 m satellite images. The width of one cell on the ground is the raster’s cell size (resolution): The width of one raster cell on the ground, such as 10 m or 100 m. Smaller cells show more detail and need more storage.. Each cell holds one value per band: One layer of values in a raster, such as the red, green or blue channel of an image, or the single layer of an elevation model.: a photograph has red, green and blue bands, an elevation model a single band. A cell with no measurement holds the band’s nodata value: A value, such as -9999, that marks a raster cell with no measurement. It is a code, not data, and must be left out of sums, means and counts., a code such as -9999 that is not data.

The cells are tied to the ground by the raster’s geotransform: Six numbers that place a raster on the ground: the x of its top-left corner, the cell width, a row rotation, the y of that corner, a column rotation and the cell height., six numbers that GDAL writes as GT(0) to GT(5). A cell’s column and row turn into map coordinates with x = GT(0) + column × GT(1) + row × GT(2) and y = GT(3) + column × GT(4) + row × GT(5), counted from the top-left corner of the top-left cell (GDAL, Raster Data Model). For a north-up image GT(2) and GT(4) are zero, GT(1) is the cell width and GT(5) the cell height. Here is a small elevation raster of 100 m cells, with one cell missing its value:

A raster of 5 rows and 6 columns of 100 m cells with elevations, one nodata cell, its top-left corner at x 5000, y 3000.Elevation in metres, 100 m cellscolumn012345row01234512515519522524523514518524529530527515521529537535529513519526nodata531526511515520523524522Black dot: the top-left corner, x 5000, y 3000Blue box and dot: cell (2, 3), centre (5350, 2750)Blue cross: the point (5480, 2515), cell (4, 4)Geotransform: (5000, 100, 0, 3000, 0, -100)

The elevation raster of the example, with the corner that its geotransform starts from

Text description of the diagram

A grid of 5 rows and 6 columns, each cell 100 m on a side, holds one elevation in metres per cell. Columns are numbered 0 to 5 from west to east along the top, and rows 0 to 4 from north to south down the left side.

Row 0 holds 512, 515, 519, 522, 524 and 523. Row 1 holds 514, 518, 524, 529, 530 and 527. Row 2 holds 515, 521, 529, 537, 535 and 529. Row 3 holds 513, 519, 526, then a cell marked nodata with a dashed outline and a grey fill, then 531 and 526. Row 4 holds 511, 515, 520, 523, 524 and 522.

A black dot marks the top-left corner of the top-left cell, at x 5000 and y 3000, where the geotransform starts. A thick blue box marks cell row 2, column 3, which holds 537 m, the highest value, with a blue dot at its centre, x 5350 and y 2750. A blue cross near the lower right corner of cell row 4, column 4 marks the point x 5480, y 2515, which falls in that cell, whose value is 524 m. The text under the grid repeats these three marks and gives the geotransform: 5000, 100, 0, 3000, 0, −100.

Its geotransform is (5000, 100, 0, 3000, 0, −100). The height is negative because rows are written from the north edge down, so y falls as the row number grows. The program below goes from a cell to the map and back, and computes two means of the elevations:

A raster: cells, a geotransform and a nodata value Python · raster/raster_cells.py
"""A raster: one value per cell of a grid, tied to the ground by six numbers (the geotransform)."""
import math

NODATA = -9999  # the value that marks a cell with no measurement
# Elevation in metres on a grid of 5 rows and 6 columns with 100 m cells; row 0 is the north edge.
ELEVATION = [
    [512, 515, 519, 522, 524, 523],
    [514, 518, 524, 529, 530, 527],
    [515, 521, 529, 537, 535, 529],
    [513, 519, 526, NODATA, 531, 526],
    [511, 515, 520, 523, 524, 522],
]
# x of the west edge, cell width, row rotation, y of the north edge, column rotation, cell height
TRANSFORM = (5_000, 100, 0, 3_000, 0, -100)


def cell_centre(row, col, t=TRANSFORM):
    """Map coordinates of the centre of a cell: the geotransform applied to (col + 0.5, row + 0.5)."""
    x = t[0] + (col + 0.5) * t[1] + (row + 0.5) * t[2]
    y = t[3] + (col + 0.5) * t[4] + (row + 0.5) * t[5]
    return x, y


def cell_at(x, y, t=TRANSFORM):
    """The (row, col) of the cell that holds (x, y), for a grid without rotation."""
    return math.floor((y - t[3]) / t[5]), math.floor((x - t[0]) / t[1])


rows, cols = len(ELEVATION), len(ELEVATION[0])
print(f"{rows} rows x {cols} columns of {TRANSFORM[1]} m cells: x {TRANSFORM[0]} to {TRANSFORM[0] + cols * TRANSFORM[1]},"
      f" y {TRANSFORM[3] + rows * TRANSFORM[5]} to {TRANSFORM[3]}")
print(f"Cell (row 2, col 3) is centred on {cell_centre(2, 3)} and holds {ELEVATION[2][3]} m")
row, col = cell_at(5_480, 2_515)
print(f"The point (5480, 2515) is in cell (row {row}, col {col}), which holds {ELEVATION[row][col]} m")
values = [v for line in ELEVATION for v in line]
valid = [v for v in values if v != NODATA]
print(f"Mean of all 30 cells, nodata counted as a value: {sum(values) / len(values):.1f} m")
print(f"Mean of the {len(valid)} cells with data: {sum(valid) / len(valid):.1f} m")
above = sum(v > 525 for v in valid)
print(f"{above} cells are above 525 m: {above * TRANSFORM[1] ** 2 / 10_000:.0f} ha")

Output

5 rows x 6 columns of 100 m cells: x 5000 to 5600, y 2500 to 3000
Cell (row 2, col 3) is centred on (5350.0, 2750.0) and holds 537 m
The point (5480, 2515) is in cell (row 4, col 4), which holds 524 m
Mean of all 30 cells, nodata counted as a value: 171.8 m
Mean of the 29 cells with data: 522.5 m
10 cells are above 525 m: 10 ha

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 raster_cells.py

Add half a cell to reach a centre: the cell in row 2, column 3 is centred on x 5000 + 3.5 × 100 = 5,350 and y 3000 − 2.5 × 100 = 2,750. Going back, divide the distance from the corner by the cell size and round down: the point x 5480, y 2515 falls in row 4, column 4.

Counting nodata as a value

The wrong way: average every cell. One cell of -9999 among 30 turned a mean elevation of 522.5 m into 171.8 m. The right way: leave out every cell that holds the band’s nodata value before any sum, mean or count, as the second mean of the program does.

Checkpoint

Check: Vector features · Rasters and their geotransform

  1. Question 1 of 3 A park's outer ring encloses 800 m by 600 m, and a pond inside it, stored as a hole, is 200 m by 200 m. What is the park's area in hectares?

    Type a number.

    A hint

    One hectare is 10,000 m², and a hole is taken away from the area of the outer ring.

    Show the answer: Question 1 of 3

    Answer: 44 ha

    The outer ring holds 480,000 m², 48 ha, and the hole 40,000 m², 4 ha. A hole's ring runs the other way round, so its signed area is negative and the sum is 48 − 4 = 44 ha, as the example printed for Lake Park.

  2. Question 2 of 3 The raster's geotransform is (5000, 100, 0, 3000, 0, -100). What is the x coordinate of the centre of the cell in row 0, column 5?

    Type a number.

    A hint

    Move from the corner's x by the column number plus half a cell, in steps of the cell width.

    Show the answer: Question 2 of 3

    Answer: 5550 m

    x = 5000 + (5 + 0.5) × 100 = 5,550. The half cell matters: x0 + col × width is the cell's west edge, 5,500, not its centre.

  3. Question 3 of 3 The example's mean elevation came out as 171.8 m when every cell was counted, but 522.5 m from the 29 cells with data. What went wrong in the first mean?

    Choose one answer.

    Show the answer: Question 3 of 3

    Answer: The nodata value, -9999, was added in as if it were an elevation

    A raster's nodata value is a marker, not a measurement. Leave those cells out of sums, means and counts, or the result mixes real values with a code: one -9999 among 30 cells turned 522.5 m into 171.8 m.

Objects or fields: the choice decides what you can ask

Some things in the world are objects, separate and countable, with edges: wells, roads, parcels, clinics. Others are fields, quantities with a value at every place: elevation, rainfall, noise. Objects suit vectors, which keep each one’s position and identity. Fields suit rasters, which hold a value everywhere, and that is where the QGIS introduction recommends them: for information that is continuous across an area and “cannot easily be divided into vector features”.

Rasters come in two kinds, and they need different maths. A continuous raster holds measurements, such as metres of elevation, which can be averaged and compared. A categorical raster holds class codes, such as 1 for water and 3 for forest, which are names: the average of 1 and 3 is 2, the code of a class that neither cell holds. Count categorical cells, never average them.

Choosing a model also chooses what you can still ask. The program below stores the same 120 street lights three ways, as points, as counts in 50 m cells and as four totals, and asks each about one house:

The same street lights in three models Python · models/lights_three_ways.py
"""The same 120 street lights as points, as a grid of 50 m cells and as four totals: what each model can answer."""
import math
import random

rng = random.Random(11)
# Lights along two streets that cross a 1 km square block: one runs east-west, one north-south.
lights = [(round(rng.uniform(0, 1_000), 1), round(500 + rng.uniform(-6, 6), 1)) for _ in range(60)]
lights += [(round(300 + rng.uniform(-6, 6), 1), round(rng.uniform(0, 1_000), 1)) for _ in range(60)]

CELL = 50
grid = {}  # (column, row) -> number of lights in that cell
for x, y in lights:
    grid[(int(x // CELL), int(y // CELL))] = grid.get((int(x // CELL), int(y // CELL)), 0) + 1
quarters = {}  # four totals, the way a report by area would store them
for x, y in lights:
    name = ("north" if y >= 500 else "south") + ("-east" if x >= 500 else "-west")
    quarters[name] = quarters.get(name, 0) + 1

house = (512.0, 531.0)
print(f"Points: the nearest light is {min(math.dist(house, p) for p in lights):.1f} m from the house,"
      f" and {sum(math.dist(house, p) <= 30 for p in lights)} are within 30 m")
cell = (int(house[0] // CELL), int(house[1] // CELL))
print(f"Grid: the house's 50 m cell {cell} holds {grid.get(cell, 0)} lights, but not where in the cell they stand")
print(f"Totals: north-east {quarters['north-east']}, north-west {quarters['north-west']},"
      f" south-east {quarters['south-east']}, south-west {quarters['south-west']}; nothing about any one street")
print(f"Stored: {len(lights)} points, {len(grid)} cells with a light out of {(1_000 // CELL) ** 2}, 4 totals")

Output

Points: the nearest light is 30.1 m from the house, and 0 are within 30 m
Grid: the house's 50 m cell (10, 10) holds 2 lights, but not where in the cell they stand
Totals: north-east 15, north-west 50, south-east 12, south-west 43; nothing about any one street
Stored: 120 points, 62 cells with a light out of 400, 4 totals

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 lights_three_ways.py

Only the points can say that the nearest light is 30.1 m away, so none is within 30 m. The grid knows that the house’s cell holds 2 lights, but not where in the cell they stand. The totals know only that the north-east quarter has 15. Each step of aggregation is cheaper to store and answers fewer questions. Decide for your own data with the table below:

Interactive · answer and compare Vectors or rasters for your data? Answer four questions about something you want to store. The table marks the models that fit and says why the others do not.
Is it a set of separate objects, or a quantity that has a value at every place?
Are its values measurements or classes?
Do exact boundaries or narrow features matter, such as a 40 m channel or the edge of a plot?
Does the data arrive as imagery or from a regular grid of sensors?

Answer the questions above: the table then marks what fits your case and says why the rest does not.

Three vector and two raster models against the four things that decide how to store a phenomenon
Model Exact edges and identity A value everywhere Narrow or small things Maths cell by cell
Points Stores: one position per object Good each object keeps its exact position and its own row Poor knows nothing between the points Good a lamp post is one point, however small Poor needs interpolation before any cell-by-cell maths
Lines Stores: a path through positions Good roads and rivers keep their exact course Poor values only along the line itself Good a 40 m inlet stays a thin line, never lost Poor must be rasterised first
Polygons Stores: the outline of an area Good boundaries stay exact: the lake is 69.39 ha Mixed one value per area, the same all over it Mixed keeps narrow parts if someone digitised them Poor overlay works; cell-by-cell maths needs a raster
Continuous raster Stores: a measured value in every cell Poor edges become staircases of cells Good elevation or noise, one value in every cell Poor the 40 m inlet vanished in 100 m cells Good add, compare and average cell by cell
Categorical raster Stores: a class code in every cell Mixed class edges follow cell edges Good a land-cover class in every cell Poor classes narrower than a cell disappear Mixed count and compare classes; never average the codes
How the answers decide
  • “Is it a set of separate objects, or a quantity that has a value at every place?” Separate objects: wells, roads, parcels: rules out Continuous raster and adds a caveat to Categorical raster. Separate objects need their own rows and exact positions; a grid of cells blurs neighbours together and loses which is which.
  • “Is it a set of separate objects, or a quantity that has a value at every place?” A value everywhere: height, rainfall: rules out Points and Lines and adds a caveat to Polygons. A quantity that exists everywhere needs a value at every place: points and lines know it only where they are, and polygons only one value per area.
  • “Are its values measurements or classes?” Classes: land-cover types: rules out Continuous raster. Class codes are labels, so averaging or smoothing them invents classes that are not there.
  • “Are its values measurements or classes?” Measurements: metres, decibels: adds a caveat to Categorical raster. Measurements fit a continuous raster; grouping them into classes throws detail away for good.
  • “Do exact boundaries or narrow features matter, such as a 40 m channel or the edge of a plot?” Yes: adds a caveat to Continuous raster and Categorical raster. A feature narrower than a cell can be lost: the 40 m inlet vanished at 100 m. Use cells no wider than the narrowest feature, or vectors.
  • “Does the data arrive as imagery or from a regular grid of sensors?” Yes: adds a caveat to Points, Lines and Polygons. Imagery is a grid already. Turning it into vectors means classifying and tracing, which can lose detail or invent edges.

Most answers sort themselves out: separate objects with exact edges become points, lines or polygons, measurements with a value everywhere become a continuous raster, and classes with a value everywhere a categorical one.

Later modules of this track meet models beyond these two. Point clouds, such as those from laser scanning, hold millions of measured points, each with a height; PDAL is “a C++ library for translating and manipulating point cloud data” (PDAL). Networks store roads as junctions and links, as in the first lesson’s drive. Discrete global grids cover the Earth with cells of fixed shapes; H3, for example, indexes the world in hexagonal cells (H3).

Checkpoint

Check: Choosing between objects and fields

  1. Question 1 of 2 The street lights are stored three ways: as points, as counts in 50 m cells and as four totals. Which question can only the points answer?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: Is there a light within 30 m of this house?

    Each step of aggregation throws something away. Points keep positions and identities; a count grid keeps how many per cell; totals keep how many per area. The nearest light was 30.1 m from the house, so the honest answer to the 30 m question is no, and only the points can give it.

  2. Question 2 of 2 A land-cover raster codes water as 1, crops as 2 and forest as 3. Someone averages the codes of four cells, 1, 1, 3 and 3, and gets 2. What does that 2 mean?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: Nothing useful; codes are labels, and their average is not a class of any of the cells

    A categorical raster stores classes, so its maths is counting: the area of each class, or the class most cells hold. Means and smooth interpolation belong to continuous rasters, where the values are measurements.

Coarser cells change the area you measure

Turning a polygon into a raster, rasterising it, needs a rule for the cells on its edge. The common one keeps a cell when its centre lies inside the polygon; rasterio’s rasterize burns in “only pixels whose center is within the polygon” unless its all_touched option is set (rasterio). Here is one lake, a polygon with a 40 m inlet, rasterised that way with 30 m and with 100 m cells:

The lake polygon over its 30 m and 100 m rasters: small cells follow the shore and part of the 40 m inlet, big cells lose the inlet.One lake, two cell sizes30 m cells: 69.03 ha, inlet kept in partinlet, 40 m wide100 m cells: 68.00 ha, inlet lostinlet, 40 m widewater cells of the rasterthe polygon: 69.39 ha

The same lake rasterised with 30 m and 100 m cells, under the polygon's dashed outline

Text description of the diagram

Two panels show the same lake, drawn twice. In each, the lake polygon is a dashed black outline: a rounded body of water about 1 km from west to east and 800 m from north to south, with a narrow inlet, 40 m wide and 400 m long, reaching east from its middle. Under the outline, the water cells of a raster are shaded light blue with blue edges, cell by cell, each cell kept when its centre lies inside the polygon.

The upper panel uses 30 m cells. Its 767 cells follow the shore closely in small steps and cover 69.03 ha, against the polygon's 69.39 ha. One row of cells runs along part of the inlet, because the inlet straddles two rows of cells and only one row has its centres inside; it keeps 1.17 of the inlet's 1.60 ha.

The lower panel uses 100 m cells. Its 68 cells form a coarse block of large steps, cover 68.00 ha, and none of them lies in the inlet: at this cell size the inlet has disappeared.

The legend names the blue cells as the water cells of the raster and the dashed line as the polygon, 69.39 ha.

The 30 m cells follow the shore in small steps and keep part of the inlet; the 100 m cells keep none of it. The program behind the figure measures the polygon and its rasters with 10, 30 and 100 m cells, under the cell-centre rule and under the rule that counts every cell the lake touches:

One lake as a polygon and as rasters of three cell sizes Python · lake/rasterise_lake.py
"""One lake, stored as a polygon and as rasters of 10, 30 and 100 m cells: what each measures."""
import math

# The lake's outline in metres, anticlockwise, with a narrow inlet 40 m wide reaching east from (1200, 500).
LAKE = [
    (200, 300), (500, 120), (900, 100), (1200, 260), (1200, 480), (1600, 480), (1600, 520), (1200, 520),
    (1180, 720), (950, 880), (600, 900), (300, 780), (150, 560), (200, 300),
]


def inside(ring, x, y):
    """Ray casting: True if (x, y) is inside the ring."""
    crossings = 0
    for (x1, y1), (x2, y2) in zip(ring, ring[1:]):
        if (y1 > y) != (y2 > y) and x < x1 + (y - y1) * (x2 - x1) / (y2 - y1):
            crossings += 1
    return crossings % 2 == 1


def shoelace(ring):
    """The area enclosed by a ring."""
    return abs(sum(x1 * y2 - x2 * y1 for (x1, y1), (x2, y2) in zip(ring, ring[1:]))) / 2


def outline_length(ring):
    return sum(math.dist(a, b) for a, b in zip(ring, ring[1:]))


def crosses(a, b, xmin, ymin, xmax, ymax):
    """True if the segment a-b meets the box (Liang-Barsky clipping)."""
    (x1, y1), (x2, y2) = a, b
    dx, dy = x2 - x1, y2 - y1
    t0, t1 = 0.0, 1.0
    for p, q in ((-dx, x1 - xmin), (dx, xmax - x1), (-dy, y1 - ymin), (dy, ymax - y1)):
        if p == 0 and q < 0:
            return False
        if p < 0:
            t0 = max(t0, q / p)
        elif p > 0:
            t1 = min(t1, q / p)
    return t0 <= t1


def rasterise(ring, cell, all_touched=False):
    """Cells of a grid aligned to (0, 0): those whose centre is inside, or with all_touched every cell the lake meets."""
    xs, ys = [x for x, _ in ring], [y for _, y in ring]
    cells = set()
    for c in range(int(min(xs) // cell), int(max(xs) // cell) + 1):
        for r in range(int(min(ys) // cell), int(max(ys) // cell) + 1):
            box = (c * cell, r * cell, (c + 1) * cell, (r + 1) * cell)
            if inside(ring, (c + 0.5) * cell, (r + 0.5) * cell) or (
                all_touched and any(crosses(a, b, *box) for a, b in zip(ring, ring[1:]))
            ):
                cells.add((c, r))
    return cells


def raster_perimeter(cells, cell):
    """Edges between a water cell and a land cell, times the cell size: the staircase outline."""
    edges = sum((c + dc, r + dr) not in cells for c, r in cells for dc, dr in ((1, 0), (-1, 0), (0, 1), (0, -1)))
    return edges * cell


area, outline = shoelace(LAKE), outline_length(LAKE)
staircase = sum(abs(x2 - x1) + abs(y2 - y1) for (x1, y1), (x2, y2) in zip(LAKE, LAKE[1:]))
print(f"Polygon:      area {area / 10_000:.2f} ha, outline {outline / 1_000:.2f} km, inlet 1.60 ha")
print(f"Its outline as a staircase, |dx| + |dy| along every side: {staircase / 1_000:.2f} km")
for cell in (10, 30, 100):
    cells = rasterise(LAKE, cell)
    a, p = len(cells) * cell**2, raster_perimeter(cells, cell)
    inlet = sum(cell**2 for c, r in cells if (c + 0.5) * cell > 1_200)  # water cells east of x = 1200
    print(f"{cell:>3} m cells: area {a / 10_000:.2f} ha ({100 * (a - area) / area:+.2f} %),"
          f" outline {p / 1_000:.2f} km ({100 * (p - outline) / outline:+.0f} %), inlet {inlet / 10_000:.2f} ha")
    touched = len(rasterise(LAKE, cell, all_touched=True)) * cell**2
    print(f"             every cell the lake touches: {touched / 10_000:.2f} ha ({100 * (touched - area) / area:+.2f} %)")

Output

Polygon:      area 69.39 ha, outline 3.84 km, inlet 1.60 ha
Its outline as a staircase, |dx| + |dy| along every side: 4.50 km
 10 m cells: area 69.42 ha (+0.04 %), outline 4.50 km (+17 %), inlet 1.60 ha
             every cell the lake touches: 72.28 ha (+4.16 %)
 30 m cells: area 69.03 ha (-0.52 %), outline 4.50 km (+17 %), inlet 1.17 ha
             every cell the lake touches: 77.67 ha (+11.93 %)
100 m cells: area 68.00 ha (-2.00 %), outline 3.60 km (-6 %), inlet 0.00 ha
             every cell the lake touches: 95.00 ha (+36.91 %)

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 rasterise_lake.py

Four things show in the output, each measured rather than assumed:

  • The area drifts, but slowly. It moves from 69.42 ha at 10 m to 69.03 ha at 30 m and 68.00 ha at 100 m, against the polygon’s 69.39 ha. Cells on the shore are all water or all land, and their errors partly cancel.
  • Narrow features vanish. The 40 m inlet keeps all of its 1.60 ha at 10 m, 1.17 ha at 30 m and nothing at 100 m, because the strip, from y 480 to 520, falls between two rows of 100 m cell centres, at 450 and 550.
  • The rule matters as much as the size. Counting every cell the lake touches, the idea behind rasterio’s all_touched option, gives 72.28 ha at 10 m and 95.00 ha at 100 m, 37 % too much, because every shore cell now counts as water.
  • The outline never comes right. A raster’s outline is a staircase of cell edges, and a staircase measures each side as its east-west plus its north-south extent. For this lake that is 4.50 km, at 10 m and at 30 m alike, 17 % more than the polygon’s 3.84 km. The 100 m outline comes out shorter, 3.60 km, only because the inlet and the western tip of the lake have dropped out of its cells. Smaller cells fix the area, not the perimeter.

Try other cell sizes

Press Edit on the program and change (10, 30, 100) to (40, 50, 60). At 40 m the inlet keeps all its 1.60 ha; at 50 m it vanishes; at 60 m it comes back as 2.52 ha, more than it really covers, because whole 60 m cells now stand in for a 40 m strip. Whether a narrow feature survives depends on where the cell centres happen to fall.

Area Converter Convert a raster's area into hectares, acres or square kilometres: 767 cells of 30 m are 690,300 m².

Checkpoint

Check: What cell size does to a measurement

  1. Question 1 of 2 The lake's inlet is 40 m wide. Why did no 100 m cell of the raster count as water in the inlet?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: A cell counts as water only when its centre is inside the lake, and no 100 m cell centre falls inside the 40 m strip

    Under the cell-centre rule, the default of rasterio's rasterize, a feature narrower than a cell can vanish. At 30 m the inlet kept 1.17 of its 1.60 ha; at 10 m all of it.

  2. Question 2 of 2 The 30 m raster of the lake has 767 water cells. What is its area in hectares? Give two decimals.

    Type a number.

    Show the answer: Question 2 of 2

    Answer: 69.03 ha (anything from 69.02 to 69.04 counts)

    Each 30 m cell is 900 m², so 767 × 900 = 690,300 m², which is 69.03 ha, as the program printed: 0.52 % less than the polygon's 69.39 ha.

Scale and accuracy travel with the data

Data made from a map keeps that map’s limits. A map scale: The ratio between a length on a map and the same length on the ground. At 1:50,000, 1 mm on the map stands for 50 m. is a ratio of lengths: at 1:50,000, 1 mm on the map is 50 m on the ground. Features too small for that scale were simplified or left out when the map was drawn, and the QGIS introduction warns that vector data digitised from a map inherits the scale problems of that map. The program below turns scales and a stated error into distances on the ground:

What a map scale and a stated error mean on the ground Python · accuracy/scale_and_accuracy.py
"""What a map scale and a stated positional accuracy mean on the ground."""
import math
import random

for scale in (10_000, 50_000, 250_000):
    print(f"1:{scale:,}: 1 mm on the map is {scale / 1_000:,.0f} m on the ground, a 0.25 mm line is {0.25 * scale / 1_000:,.1f} m wide")

RMSE = 12.5  # the boundary layer's stated horizontal error in metres, as a root mean square distance
sigma = RMSE / math.sqrt(2)  # the same error split equally between the east and north directions
rng = random.Random(5)
errors = sorted(math.hypot(rng.gauss(0, sigma), rng.gauss(0, sigma)) for _ in range(100_000))
print(f"With an RMSE of {RMSE} m, 95 % of 100,000 simulated errors are within {errors[94_999]:.1f} m"
      f" (by formula {RMSE * math.sqrt(-math.log(0.05)):.1f} m)")
for d in (5, 10, 20, 30):
    chance = 0.5 * math.erfc(d / (sigma * math.sqrt(2)))  # the error across the line must exceed d
    print(f"A house {d} m inside the drawn boundary is truly on the other side with a chance of {100 * chance:.2f} %")

Output

1:10,000: 1 mm on the map is 10 m on the ground, a 0.25 mm line is 2.5 m wide
1:50,000: 1 mm on the map is 50 m on the ground, a 0.25 mm line is 12.5 m wide
1:250,000: 1 mm on the map is 250 m on the ground, a 0.25 mm line is 62.5 m wide
With an RMSE of 12.5 m, 95 % of 100,000 simulated errors are within 21.7 m (by formula 21.6 m)
A house 5 m inside the drawn boundary is truly on the other side with a chance of 28.58 %
A house 10 m inside the drawn boundary is truly on the other side with a chance of 12.89 %
A house 20 m inside the drawn boundary is truly on the other side with a chance of 1.18 %
A house 30 m inside the drawn boundary is truly on the other side with a chance of 0.03 %

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 scale_and_accuracy.py

At 1:50,000 a pen line 0.25 mm wide is itself 12.5 m wide on the ground. Accuracy is stated separately, often as an RMSE (root mean square error): The square root of the mean of the squared errors. For positions, the typical size of the distance between where things are recorded and where they really are., the root mean square of the distances between recorded and true positions. The program takes a boundary layer that states 12.5 m RMSE and assumes its errors are random, alike in every direction and independent from point to point. Then 95 % of position errors fall within 21.6 m, which it computes as the RMSE times √(−ln 0.05) and confirms with 100,000 simulated errors (21.7 m). That factor, 1.7308, is the one the US National Standard for Spatial Data Accuracy uses to report horizontal accuracy at 95 % confidence (FGDC).

The last four lines answer the question such data is often asked to decide. A house 30 m inside the drawn boundary is safely inside, with a 0.03 % chance of being on the other side. A house 10 m inside has a 12.89 % chance, and one 5 m inside 28.58 %. A boundary like this is right for district maps and statistics, and wrong for deciding which side of it a particular house stands. That needs data captured at a larger scale, such as a parcel survey.

Key idea

Every layer carries the scale and the accuracy it was made with. Use it for questions much larger than its error, and fetch better data for decisions within a few tens of metres of its lines.

Checkpoint

Check: Scale and positional accuracy

  1. Question 1 of 2 On a 1:50,000 map, a boundary is drawn with a line 0.4 mm wide. How wide is that line on the ground, in metres?

    Type a number.

    Show the answer: Question 1 of 2

    Answer: 20 m

    At 1:50,000, 1 mm on the map is 50,000 mm, or 50 m, on the ground, so 0.4 mm is 20 m. The line alone is wider than a house, before any error in where it was drawn.

  2. Question 2 of 2 A boundary layer states a horizontal error of 12.5 m RMSE. A house stands 10 m inside the drawn boundary, and a tax depends on which side it is. What does the example's model say, and what should you do?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: There is about a 13 % chance that the house is really on the other side, so check against data captured for parcels before deciding

    Positional accuracy travels with the data. With 12.5 m RMSE, 95 % of errors are within about 21.6 m, so decisions within a few tens of metres of a line need data captured at a larger scale, such as a parcel survey.

Practise: compute with rasters

Turn this lesson’s raster arithmetic into code in two steps, both tested on the page. One measures the area of each class in a land-cover raster and leaves the nodata cells out; the other goes from a cell to the map and back with a geotransform, including points on the line between two cells and points just outside the raster:

Code lab · 2 steps

Compute with rasters

Measure the area of each class of a land-cover raster, then move between cells and map coordinates with a geotransform.

  1. Measure the area of each class in a land-cover raster To do
  2. Go from a cell to the map and back with a geotransform To do

Exercise · Easy · Python

Measure the area of each class in a land-cover raster

Write area_by_class(grid, cell_size, nodata) in classes.py. grid is a categorical raster as a list of rows, each row a list of class codes such as 1 for water and 2 for forest; cell_size is the width of a square cell in metres; nodata is the code that marks cells with no data. Return a dictionary {class code: area in hectares} with one entry for each class that occurs, and none for nodata: those cells are unknown, not a class.

One hectare is 10,000 m², so a 30 m cell is 0.09 ha. The sample tests check a small grid, nodata cells, a grid with only nodata, and the area of a class in 10 m, 30 m and 100 m cells.

Starter code · classes.py

def area_by_class(grid, cell_size, nodata):
    """{class code: area in hectares} of a categorical raster; cells holding nodata are left out."""
    # Replace this line: count the cells of each class, then turn the counts into hectares.
    return {}
The sample tests · test_classes.py
from classes import area_by_class

GRID = [
    [1, 1, 2, 2],
    [1, 3, 2, 2],
    [0, 3, 3, 2],
]


def test_each_class_gets_its_area():
    """counts the cells of each class and turns them into hectares (100 m cells are 1 ha each)"""
    assert area_by_class(GRID, 100, 0) == {1: 3.0, 2: 5.0, 3: 3.0}


def test_nodata_is_left_out():
    """leaves out the nodata code, whatever number it is"""
    got = area_by_class([[255, 1], [1, 255]], 30, 255)
    assert set(got) == {1}
    assert abs(got[1] - 0.18) < 1e-9


def test_only_nodata():
    """returns an empty dictionary when every cell is nodata"""
    assert area_by_class([[-9999, -9999]], 10, -9999) == {}


def test_cell_size_matters():
    """gives 0.01 ha per 10 m cell, 0.09 ha per 30 m cell and 1 ha per 100 m cell"""
    water = [[1] * 10 for _ in range(10)]  # 100 cells of water
    for size, hectares in [(10, 1.0), (30, 9.0), (100, 100.0)]:
        assert abs(area_by_class(water, size, 0)[1] - hectares) < 1e-9
A hint

Count the cells of each code first, skipping any cell equal to nodata. Then turn each count into an area: a count times the area of one cell, cell_size * cell_size square metres, divided by 10,000 to give hectares.

The sample tests run on this device, in your browser (Pyodide): nothing is sent to mysmartcopilot.com. The first run downloads Python (about 13.5 MB), which is kept for the next runs. A check in your browser is feedback for you, not proof that the code is right for every input.

Exercise · Easy · Python

Go from a cell to the map and back with a geotransform

Write two functions in cells.py for a north-up raster, whose geotransform is (x0, width, 0, y0, 0, height): x0, y0 is the top-left corner of the top-left cell, width is the cell width and height is the cell height, a negative number because rows count southwards.

  • cell_centre(row, col, transform) returns the (x, y) of the centre of a cell.
  • cell_at(x, y, transform, rows, cols) returns the (row, col) of the cell that holds a point, or None when the point is outside the raster of rows rows and cols columns. A point on the line between two cells belongs to the cell east or south of it, which is what rounding the cell numbers down gives.

The sample tests use the lesson's elevation raster, (5000, 100, 0, 3000, 0, -100) with 5 rows and 6 columns, and a raster of 30 m cells.

Starter code · cells.py

import math


def cell_centre(row, col, transform):
    """(x, y) of the centre of a cell of a north-up raster with the geotransform (x0, width, 0, y0, 0, height)."""
    # Replace this line: start at the corner (x0, y0) and move half a cell further than col and row.
    return 0, 0


def cell_at(x, y, transform, rows, cols):
    """(row, col) of the cell that holds (x, y), or None when the point is outside the raster."""
    # Replace this line: divide the distance from the corner by the cell size and round down.
    return None
The sample tests · test_cells.py
from cells import cell_at, cell_centre

DEM = (5_000, 100, 0, 3_000, 0, -100)  # the lesson's elevation raster: 5 rows, 6 columns


def test_centres_of_the_lesson_raster():
    """puts cell (2, 3) at (5350, 2750) and cell (0, 0) half a cell from the corner"""
    assert cell_centre(2, 3, DEM) == (5_350, 2_750)
    assert cell_centre(0, 0, DEM) == (5_050, 2_950)
    assert cell_centre(4, 5, DEM) == (5_550, 2_550)


def test_points_inside():
    """finds the cell of a point, rows counting southwards"""
    assert cell_at(5_480, 2_515, DEM, 5, 6) == (4, 4)
    assert cell_at(5_001, 2_999, DEM, 5, 6) == (0, 0)


def test_points_on_cell_edges():
    """puts a point on a line between cells into the cell east or south of it"""
    assert cell_at(5_100, 2_950, DEM, 5, 6) == (0, 1)
    assert cell_at(5_050, 2_900, DEM, 5, 6) == (1, 0)
    assert cell_at(5_000, 3_000, DEM, 5, 6) == (0, 0)


def test_points_outside():
    """returns None beyond every edge, just west of the raster included"""
    for x, y in [(4_990, 2_950), (5_600, 2_950), (5_050, 3_001), (5_050, 2_500), (9_000, 0)]:
        assert cell_at(x, y, DEM, 5, 6) is None, (x, y)


def test_another_raster():
    """works for any cell size: a raster of 30 m cells with its corner at (0, 600)"""
    small = (0, 30, 0, 600, 0, -30)
    assert cell_centre(0, 0, small) == (15, 585)
    assert cell_at(61, 539, small, 20, 20) == (2, 2)
A hint

The centre of a cell is half a cell in from its corner: x0 + (col + 0.5) * width and y0 + (row + 0.5) * height. Going back, divide the distance from the corner by the cell size and round down with math.floor, not int: int rounds towards zero, which puts a point just west of the raster into column 0. Then check that the row and column are inside the raster.

The sample tests run on this device, in your browser (Pyodide): nothing is sent to mysmartcopilot.com. The first run downloads Python (about 13.5 MB), which is kept for the next runs. A check in your browser is feedback for you, not proof that the code is right for every input.

A short self-check follows, over the five ideas from features to scale, and it opens with the idea you have answered least well:

Practice that adapts to you

Practise this lesson

14 questions about 5 ideas of this lesson. Each answer is explained at once, and what you get right is kept in this browser.

  1. Question 1 of 14 Which list gives the three basic vector geometry types, from fewest to most points needed?

    Choose one answer.

    Show the answer: Question 1 of 14

    Answer: Point, line, polygon

    Vector features are points, lines and polygons, and their multi-part forms. A polygon's ring repeats its first point at the end to close, so a triangle is written with four pairs.

  2. Question 2 of 14 What does this well-known text describe: POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0), (4 4, 4 6, 6 6, 6 4, 4 4))?

    Choose one answer.

    Show the answer: Question 2 of 14

    Answer: A 10 by 10 square with a 2 by 2 hole in its middle, so an area of 96

    In well-known text, a POLYGON lists its rings in brackets, the outer ring first and any holes after it. Here the outer ring encloses 100 and the hole 4, so the polygon's area is 96.

  3. Question 3 of 14 Which set of facts, together with the cell values, tells software where every cell of a north-up raster lies on the ground?

    Choose one answer.

    Show the answer: Question 3 of 14

    Answer: The corner coordinates, the cell width and height, and the coordinate reference system

    GDAL's geotransform holds six numbers: the corner's x, the cell width, a row rotation, the corner's y, a column rotation and the cell height. For a north-up raster the rotations are 0, and the coordinate reference system says what the coordinates measure.

  4. Question 4 of 14 With the geotransform (5000, 100, 0, 3000, 0, -100), which cell holds the point x 5150, y 2950?

    Choose one answer.

    Show the answer: Question 4 of 14

    Answer: Row 0, column 1

    Divide the distance from the corner by the cell size and round down: x gives the column, and y, divided by the negative cell height, gives the row counted southwards.

  5. Question 5 of 14 A colleague writes a raster with the geotransform (5000, 100, 0, 2500, 0, 100), a positive cell height, by mistake for a grid whose top edge is at y 3000. What happens when it is drawn?

    Choose one answer.

    Show the answer: Question 5 of 14

    Answer: Its rows are placed northwards from y 2500, so the grid appears flipped upside down

    For north-up data written row by row from the north, the cell height is negative: y = y0 + (row + 0.5) × height falls as the row number grows. A positive height lays the same rows out northwards, mirroring the picture.

  6. Question 6 of 14 Which of these is a field, a quantity with a value at every place, rather than a set of objects?

    Choose one answer.

    Show the answer: Question 6 of 14

    Answer: Air temperature across a city

    Objects (stops, roads, wards) suit vectors; fields (temperature, elevation, noise) suit continuous rasters, or samples from which a surface is interpolated.

  7. Question 7 of 14 A city wants a map of noise from 900 fixed sensors, so that anyone can read the level at their own street. How should the result be stored?

    Choose one answer.

    Show the answer: Question 7 of 14

    Answer: Keep the sensors as points, and interpolate a continuous raster for the map

    The samples stay as points, because that is where they were measured. A field needs a value everywhere, so interpolation turns the points into a continuous raster, the surface question of the first lesson.

  8. Question 8 of 14 A land-cover raster with classes 1 (water) and 3 (forest) is shrunk to coarser cells by averaging neighbouring values. Some new cells hold 2, a class meaning crops. Why?

    Choose one answer.

    Show the answer: Question 8 of 14

    Answer: Averaging treats class codes as numbers, and the average of 1 and 3 is 2, a class that is not there

    Continuous rasters can be averaged; categorical ones cannot, because their numbers are names. Resampling a categorical raster by nearest neighbour or by the most common class keeps every code a real class.

  9. Question 9 of 14 As the cells get smaller, what happens to the area measured from the lake's raster?

    Choose one answer.

    Show the answer: Question 9 of 14

    Answer: It gets closer to the polygon's area, 69.39 ha

    Each cell on the shore is all water or all land, so the raster's area misses some shore and adds some. The errors shrink with the cells, and the area converges to the polygon's.

  10. Question 10 of 14 The raster outline of the lake was 4.50 km at both 10 m and 30 m, while the polygon's outline is 3.84 km. Why do smaller cells not fix it?

    Choose one answer.

    Show the answer: Question 10 of 14

    Answer: A staircase of cell edges measures each side as its east-west plus its north-south extent, 4.50 km for this lake, however small the cells get

    Area converges as cells shrink, but a raster's outline does not: it tends to the sum of |dx| + |dy| along the shore, 17 % more than the true 3.84 km here. Measure lengths and perimeters on vectors.

  11. Question 11 of 14 A water agency must map channels that are 15 m wide or more, and no channel may vanish. Which cell size is the largest it can safely use under the cell-centre rule?

    Choose one answer.

    Show the answer: Question 11 of 14

    Answer: 15 m: no wider than the narrowest channel, so every row or column of cells that crosses a channel has a cell centre inside it

    Choose cells by the narrowest thing you must keep, not by the area error alone. With cells no wider than the narrowest channel, each crossing holds at least one cell centre, so no channel vanishes, though its width can be off by up to a cell. Smaller cells keep its shape closer, and vector lines keep it at any scale.

  12. Question 12 of 14 What does a scale of 1:50,000 mean?

    Choose one answer.

    Show the answer: Question 12 of 14

    Answer: One unit on the map stands for 50,000 of the same unit on the ground

    A scale is a ratio of lengths. Data compiled at 1:50,000 inherits the detail and the drawing limits of that scale: features simplified for the map, and lines that are themselves tens of metres wide.

  13. Question 13 of 14 A layer states a horizontal RMSE of 10 m. Using the lesson's formula, within what radius do 95 % of its position errors fall? Give one decimal.

    Type a number.

    A hint

    The lesson's program multiplied the RMSE by the square root of −ln 0.05.

    Show the answer: Question 13 of 14

    Answer: 17.3 m (anything from 17.2 to 17.4 counts)

    √(−ln 0.05) ≈ 1.7308, so 10 × 1.7308 ≈ 17.3 m. For 12.5 m RMSE the program gave 21.6 m by the formula and 21.7 m from 100,000 simulated errors.

  14. Question 14 of 14 A district office wants to decide which of 2,000 houses pay a fee that applies inside a town boundary compiled at 1:50,000. What is the sound approach?

    Choose one answer.

    Show the answer: Question 14 of 14

    Answer: Use the layer for the map, but decide houses within a few tens of metres of the line from parcel records or a survey

    Scale and accuracy travel with the data: a 1:50,000 boundary is right for district maps and wrong for deciding which side of it a house stands, when the house is within the error band.

Interview questions

Warm-up (fresher): When would you store data as a raster rather than as vectors?

Store it as a raster when the thing has a value at every place, such as elevation, temperature or rainfall, or when it arrives as a grid already, as satellite and aerial images do. Rasters make cell-by-cell maths easy: adding layers, comparing them, averaging within an area. Store it as vectors when the things are separate objects with exact edges and identities, such as parcels, roads or clinics, or when lengths and boundaries must be exact: a lake polygon measured 69.39 ha, while 100 m cells gave 68.00 ha and lost a 40 m inlet. Many projects use both, for example a land-cover raster summarised inside ward polygons.

The lake and the 1:50,000 boundary come back in two longer questions; for each, the bank names what it tests and gives a first step:

Model answers open with any pass: ₹79 for 1 week ($2.99), one payment that never renews.

  1. Fresher to mid

    From the same survey, one analyst reports a lake's area as 69.4 ha from its shoreline polygon, and another as 68.0 ha from a 100 m land-and-water raster. Which do you trust, why do the numbers differ, and what do you tell the team?

    Model answer, Pro. Model answers open with any pass; prices are in the notice on this page. First step and follow-ups

  2. Mid to senior

    A council wants to charge a fee to every building inside a town boundary that was digitised from 1:50,000 maps and states a horizontal error of 12.5 m RMSE. What risks do you raise, and how would you decide the buildings near the line?

    Model answer, Pro. First step and follow-ups

Flashcards · 8 cards

Recall it, then check

Say the answer to yourself before you look: recalling it is what makes it stick.

  1. What is a feature in a vector layer?

    One thing in the layer, such as a ward, a road or a clinic: a geometry (a point, a line or a polygon) kept together with a row of attributes. A vector layer is in effect a table in which every row carries a shape.

  2. How should code work out the area of a polygon with a hole, such as a park with a pond in it?

    Take the outer ring's area and subtract the area of each inner ring. GeoJSON asks for anticlockwise outer rings and clockwise holes but tells readers to accept other windings, so the sign of a ring's area cannot be trusted.

  3. How does a raster's geotransform turn a cell's column and row into map coordinates?

    x = GT(0) + col·GT(1) + row·GT(2) and y = GT(3) + col·GT(4) + row·GT(5), counted from the top-left corner of the grid. In a north-up image GT(2) and GT(4) are 0, and GT(5) is negative because rows run southwards. Add half a cell to reach a cell's centre.

  4. What does a nodata value such as −9999 do to a mean if it is averaged in?

    It wrecks it: a single −9999 cell in a grid of 30 pulled a 522.5 m mean elevation down to 171.8 m. Leave every cell that holds the band's nodata value out of sums, means and counts.

  5. Which data model suits objects, and which suits fields?

    Separate, countable things with edges, such as wells, roads and parcels, suit vectors, which keep each one's position and identity. Quantities that have a value everywhere, such as elevation, rainfall or noise, suit rasters.

  6. Why is a categorical raster counted rather than averaged?

    Its cells hold class codes, which are names: the mean of 1 (water) and 3 (forest) is 2, a class that neither cell holds. Count cells per class; only a continuous raster of measurements can be averaged.

  7. What do coarser cells do to a lake's area and outline when it is rasterised?

    The area drifts slowly, from 69.39 ha as a polygon to 68.00 ha with 100 m cells, but narrow parts such as a 40 m inlet can vanish. The staircase outline stays too long however small the cells are: smaller cells fix the area, not the perimeter.

  8. Why should a boundary compiled at 1:50,000 with 12.5 m RMSE not decide which side of it one house stands?

    Its lines are good only to tens of metres: with random errors, 95 % of positions fall within 21.6 m, and a house 10 m inside the line has about a 13 % chance of lying outside. Use such data for district statistics and a parcel survey for single houses.

Key takeaways

  • A vector feature is a geometry, a point, a line or a polygon with optional holes, stored with a row of attributes, and well-known text writes the geometry as one line of text.
  • A raster is a grid of equal cells with one value per band, tied to the ground by its geotransform, and cells holding the nodata value must be left out of every statistic.
  • Objects suit vectors and fields suit rasters; categorical rasters are counted, never averaged, and every step of aggregation answers fewer questions.
  • Coarser cells make a raster’s area drift and make narrow features vanish, and a raster’s staircase outline never shrinks to the true shoreline, however small the cells get.
  • Data keeps the scale and the accuracy it was made with: a 1:50,000 boundary with 12.5 m RMSE can put a house 10 m inside it on the wrong side about one time in eight.

Terms in this lesson

attribute
A value stored with a feature that is not part of its shape, such as a name or a count. The attributes of a layer's features form its attribute table.
band
One layer of values in a raster, such as the red, green or blue channel of an image, or the single layer of an elevation model.
cell size (resolution)
The width of one raster cell on the ground, such as 10 m or 100 m. Smaller cells show more detail and need more storage.
feature
One thing in a vector layer, such as a ward, a road or a clinic: a geometry stored together with a row of attributes.
geotransform
Six numbers that place a raster on the ground: the x of its top-left corner, the cell width, a row rotation, the y of that corner, a column rotation and the cell height.
map scale
The ratio between a length on a map and the same length on the ground. At 1:50,000, 1 mm on the map stands for 50 m.
nodata value
A value, such as -9999, that marks a raster cell with no measurement. It is a code, not data, and must be left out of sums, means and counts.
raster
A grid of equal cells, each holding one value per band, placed on the ground by a geotransform and a coordinate reference system. It suits imagery and quantities with a value everywhere.
RMSE (root mean square error)
The square root of the mean of the squared errors. For positions, the typical size of the distance between where things are recorded and where they really are.
well-known text (WKT)
A plain-text form of a geometry from the OGC Simple Features standard, such as POINT (4000 5000). The same name is also used for a text format of coordinate reference systems.

References

Related tools

Report a problem with this lesson

Quick answers and tool search

Type to search tools or to get a quick answer, for example 18% of 2500. Use the up and down arrow keys to move through the results, Enter to choose, and Escape to close.