Imagery Service Analysis

Analysis of OpenStreetMap changesets that use imagery services from the imagery_used column. The imagery tag is set automatically by iD, Vespucci and Go Map!!. As other editors are not using it and iD is vastly more popular than other relevant editors this graph is very close to 'market share of iD by edit volume'. JOSM users are typically using source field to note actually used sources.

Monthly Percentage of Edits and Contributors Using Imagery Services

code
df = duckdb.sql("""
WITH monthly_with_imagery AS (
    SELECT 
        year,
        month,
        CONCAT(year, '-', LPAD(CAST(month as VARCHAR), 2, '0')) as months,
        COUNT(DISTINCT user_name) as contributors_with_imagery,
        SUM(edit_count) as edits_with_imagery
    FROM '../changeset_data/year=*/month=*/*.parquet'
    WHERE imagery_used IS NOT NULL
    GROUP BY year, month
),
monthly_total AS (
    SELECT 
        year,
        month,
        CONCAT(year, '-', LPAD(CAST(month as VARCHAR), 2, '0')) as months,
        COUNT(DISTINCT user_name) as total_contributors,
        SUM(edit_count) as total_edits
    FROM '../changeset_data/year=*/month=*/*.parquet'
    GROUP BY year, month
)
SELECT 
    mt.months,
    COALESCE(ROUND((mwi.contributors_with_imagery * 100.0) / mt.total_contributors, 2), 0) as 'Percentage Contributors with Imagery',
    COALESCE(ROUND((mwi.edits_with_imagery * 100.0) / mt.total_edits, 2), 0) as 'Percentage Edits with Imagery'
FROM monthly_total mt
LEFT JOIN monthly_with_imagery mwi ON mt.year = mwi.year AND mt.month = mwi.month
ORDER BY mt.year, mt.month
""").df()

util.show_figure(
    [
        util.FigureConfig(
            title="Monthly Percentage of Contributors Using Imagery Services",
            label="Contributors",
            x_col="months",
            y_col="Percentage Contributors with Imagery",
            y_unit_hover_template="%",
            query_or_df=df,
        ),
        util.FigureConfig(
            title="Monthly Percentage of Edits Using Imagery Services",
            label="Edits",
            x_col="months",
            y_col="Percentage Edits with Imagery",
            y_unit_hover_template="%",
            query_or_df=df,
        ),
    ]
)

Monthly Top 10 Imagery Services: Edits, Accumulated Edits, Contributors and Accumulated Contributors

code
# Get top 10 imagery services by total edits
df = duckdb.sql("""
WITH imagery_expanded AS (
    SELECT 
        year,
        month,
        user_name,
        edit_count,
        unnest(imagery_used) as imagery_service
    FROM '../changeset_data/year=*/month=*/*.parquet'
    WHERE imagery_used IS NOT NULL
),
top_imagery AS (
    SELECT imagery_service
    FROM (
        SELECT
            imagery_service,
            SUM(edit_count) as total_edits
        FROM imagery_expanded
        GROUP BY imagery_service
        ORDER BY total_edits DESC
        LIMIT 10
    )
),
monthly_imagery_data AS (
    SELECT 
        ie.year,
        ie.month,
        CONCAT(ie.year, '-', LPAD(CAST(ie.month as VARCHAR), 2, '0')) as months,
        ie.imagery_service,
        COUNT(DISTINCT ie.user_name) as "Contributors",
        SUM(ie.edit_count) as "Edits"
    FROM imagery_expanded ie
    WHERE ie.imagery_service IN (SELECT imagery_service FROM top_imagery)
    GROUP BY ie.year, ie.month, ie.imagery_service
)
SELECT 
    months,
    imagery_service,
    "Contributors",
    "Edits",
    SUM("Contributors") OVER (PARTITION BY imagery_service ORDER BY year, month) as "Contributors Accumulated",
    SUM("Edits") OVER (PARTITION BY imagery_service ORDER BY year, month) as "Edits Accumulated"
FROM monthly_imagery_data
ORDER BY year, month, imagery_service
""").df()

util.show_figure(
    [
        util.FigureConfig(
            title="Monthly Edits by Top 10 Imagery Services",
            label="Edits",
            x_col="months",
            y_col="Edits",
            group_col="imagery_service",
            query_or_df=df,
        ),
        util.FigureConfig(
            title="Accumulated Edits by Top 10 Imagery Services",
            label="Edits Accumulated",
            x_col="months",
            y_col="Edits Accumulated",
            group_col="imagery_service",
            query_or_df=df,
        ),
        util.FigureConfig(
            title="Monthly Contributors by Top 10 Imagery Services",
            label="Contributors",
            x_col="months",
            y_col="Contributors",
            group_col="imagery_service",
            query_or_df=df,
        ),
        util.FigureConfig(
            title="Accumulated Contributors by Top 10 Imagery Services",
            label="Contributors Accumulated",
            x_col="months",
            y_col="Contributors Accumulated",
            group_col="imagery_service",
            query_or_df=df,
        ),
    ]
)

Top 100 Imagery Services Yearly

code
import json

# Load replacement rules for clickable links
with open("../config/replace_rules_imagery_and_source.json") as f:
    imagery_name_to_html_link = {
        name: f'<a href="{item["link"]}">{name}</a>' for name, item in json.load(f).items() if "link" in item
    }

query = """
WITH imagery_expanded AS (
    SELECT 
        year,
        user_name,
        edit_count,
        unnest(imagery_used) as imagery_service
    FROM '../changeset_data/year=*/month=*/*.parquet'
    WHERE imagery_used IS NOT NULL
),
user_first_year AS (
    SELECT 
        user_name,
        imagery_service,
        MIN(year) as first_year
    FROM imagery_expanded
    GROUP BY user_name, imagery_service
),
imagery_totals AS (
    SELECT
        imagery_service as "Imagery Service",
        CAST(SUM(edit_count) as BIGINT) as total_edits_all_time,
        CAST(SUM(CASE WHEN year >= 2021 THEN edit_count ELSE 0 END) as BIGINT) as total_edits_2021_now,
        CAST(COUNT(DISTINCT user_name) as BIGINT) as total_contributors_all_time,
        CAST(COUNT(DISTINCT CASE WHEN year >= 2021 THEN user_name END) as BIGINT) as total_contributors_2021_now
    FROM imagery_expanded
    GROUP BY imagery_service
),
yearly_metrics AS (
    SELECT
        ie.year,
        ie.imagery_service as "Imagery Service",
        CAST(SUM(ie.edit_count) as BIGINT) as "Edits",
        CAST(COUNT(DISTINCT ie.user_name) as BIGINT) as "Contributors",
        CAST(COUNT(DISTINCT CASE WHEN ufy.first_year = ie.year THEN ie.user_name END) as BIGINT) as "New Contributors"
    FROM imagery_expanded ie
    LEFT JOIN user_first_year ufy 
        ON ie.user_name = ufy.user_name AND ie.imagery_service = ufy.imagery_service
    GROUP BY ie.year, ie.imagery_service
)
SELECT 
    ym.year,
    ym."Imagery Service",
    ym."Edits",
    ym."New Contributors",
    ym."Contributors",
    it.total_edits_all_time as "Total Edits",
    it.total_edits_2021_now as "Total Edits (2021 - Now)",
    it.total_contributors_all_time as "Total Contributors",
    it.total_contributors_2021_now as "Total Contributors (2021 - Now)"
FROM yearly_metrics ym
JOIN imagery_totals it
    ON ym."Imagery Service" = it."Imagery Service"
ORDER BY year DESC, "Edits" DESC
"""
df = duckdb.sql(query).df()

# Apply HTML links to imagery service names
df["Imagery Service"] = df["Imagery Service"].apply(
    lambda name: imagery_name_to_html_link[name] if name in imagery_name_to_html_link else name
)

top_100_contributors = df.groupby("Imagery Service")["Total Contributors"].first().nlargest(100)
top_100_contributors_2021_now = df.groupby("Imagery Service")["Total Contributors (2021 - Now)"].first().nlargest(100)
top_100_edits = df.groupby("Imagery Service")["Total Edits"].first().nlargest(100)
top_100_edits_2021_now = df.groupby("Imagery Service")["Total Edits (2021 - Now)"].first().nlargest(100)

table_configs = [
    util.TableConfig(
        title="Top 100 Imagery Services by Contributors",
        query_or_df=df[df["Imagery Service"].isin(top_100_contributors.index)],
        x_axis_col="year",
        y_axis_col="Imagery Service",
        value_col="Contributors",
        center_columns=["Rank", "Imagery Service"],
        sum_col="Total Contributors",
    ),
    util.TableConfig(
        title="Top 100 Imagery Services by Contributors 2021 - Now",
        query_or_df=df[(df["Imagery Service"].isin(top_100_contributors_2021_now.index)) & (df["year"] >= 2021)],
        x_axis_col="year",
        y_axis_col="Imagery Service",
        value_col="Contributors",
        center_columns=["Rank", "Imagery Service"],
        sum_col="Total Contributors (2021 - Now)",
    ),
    util.TableConfig(
        title="Top 100 Imagery Services by Edits",
        query_or_df=df[df["Imagery Service"].isin(top_100_edits.index)],
        x_axis_col="year",
        y_axis_col="Imagery Service",
        value_col="Edits",
        center_columns=["Rank", "Imagery Service"],
        sum_col="Total Edits",
    ),
    util.TableConfig(
        title="Top 100 Imagery Services by Edits 2021 - Now",
        query_or_df=df[(df["Imagery Service"].isin(top_100_edits_2021_now.index)) & (df["year"] >= 2021)],
        x_axis_col="year",
        y_axis_col="Imagery Service",
        value_col="Edits",
        center_columns=["Rank", "Imagery Service"],
        sum_col="Total Edits (2021 - Now)",
    ),
]

util.show_tables(table_configs)
Rank Imagery Service 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 Total Contributors
1 Bing Aerial Imagery 55,606 123,437 130,064 134,295 166,720 174,812 155,602 169,325 151,032 126,243 148,789 153,769 149,334 131,162 1,409,650
2 .gpx data file 835 4,243 7,927 28,777 45,339 50,670 47,816 76,173 82,985 52,319 45,790 11,851 10,275 8,897 371,096
3 Esri World Imagery 0 0 0 1 4,439 14,942 19,510 25,003 23,783 21,113 34,686 34,227 35,613 29,615 163,816
4 Maxar Imagery 0 0 0 0 0 0 17,936 35,634 53,021 48,224 30,163 221 11 5 150,597
5 Custom 345 1,807 4,729 9,193 19,536 12,985 14,523 37,108 30,860 4,339 2,912 3,622 3,433 3,460 130,857
6 OpenStreetMap (Standard) 0 0 0 1,444 10,559 12,257 12,768 11,741 13,993 13,432 14,584 14,558 14,863 12,241 93,834
7 Mapbox Satellite 0 4,168 6,217 9,999 9,165 7,459 8,491 10,857 14,309 11,113 17,925 11,016 11,441 15,117 93,715
8 .geojson data file 0 0 0 0 9 87 165 572 334 320 9,326 36,367 25,656 17,343 80,235
9 BDOrtho 0 0 0 0 0 9,870 12,262 14,168 13,363 12,923 13,228 13,109 9,080 75 60,879
10 DigitalGlobe Imagery (now Maxar) 0 0 0 1 14,193 19,685 12,718 14 3 3 0 0 0 0 38,697
11 GPS 912 4,325 4,547 4,323 5,420 5,156 5,271 5,187 4,391 3,644 3,619 3,550 3,766 3,276 38,690
12 None 308 1,684 1,811 1,735 1,538 1,867 2,690 2,641 2,892 3,793 2,522 3,203 3,364 5,739 28,383
13 Geoportal 2: Orthophotomap 0 0 0 0 1 3,265 4,817 5,052 3,270 5,698 5,749 6,105 6,554 6,297 28,149
14 PNOA Spain 0 0 0 256 4,186 4,283 4,613 3,489 4,190 4,560 3,751 3,582 3,719 3,198 25,661
15 MAPNIK 1,117 4,851 6,283 8,281 255 115 48 4,155 40 9 11 3 0 0 23,041
16 Mapillary 0 0 0 1,551 3,253 3,297 3,083 3,431 3,380 2,724 2,720 2,623 3,066 2,694 21,606
17 basemap.at 13 171 391 2,708 2,983 3,191 3,101 3,351 3,418 3,495 3,511 3,631 3,857 3,640 21,324
18 Bavaria (80 cm) 0 0 0 43 259 3,656 4,059 4,602 4,769 4,663 4,705 3,715 17 3 18,627
19 PDOK aerial imagery 0 0 0 0 0 2,229 3,354 2,854 3,193 3,199 3,035 3,287 3,798 3,516 18,003
20 BD Ortho IGN 0 0 0 0 0 0 0 0 0 0 0 0 8,481 12,623 17,417
21 OpenTopoMap 0 0 0 0 1 182 2,358 3,290 2,971 2,099 2,591 2,694 2,737 2,645 16,154
22 Geoportal.gov.pl (Orthophotomap) 0 262 1,095 4,496 4,887 3,227 139 15 0 1 1 1 0 0 10,733
23 swisstopo SWISSIMAGE 0 0 0 0 0 0 0 0 1,315 2,456 2,616 2,714 3,150 2,737 9,673
24 NRW Orthophoto 0 0 0 0 0 0 0 1,176 4,453 4,371 3,141 514 25 1 9,390
25 Saxony latest aerial imagery 0 0 0 0 0 1,299 1,506 1,309 1,657 1,609 1,763 1,813 1,887 1,614 8,194
26 NRW Orthophoto (RGB) 0 0 0 0 0 0 0 0 0 0 0 2,125 4,757 3,651 8,006
27 Hesse DOP20 0 0 0 0 0 0 0 0 0 2,115 2,262 2,411 2,566 2,200 7,542
28 OpenStreetCam Images 0 0 0 0 363 2,240 2,124 2,248 2,200 410 12 1 2 0 7,142
29 Norway Orthophoto 0 0 0 0 0 780 1,167 1,016 1,166 1,218 1,211 1,277 1,490 1,377 6,735
30 MML Orthophoto 0 0 0 0 0 961 1,104 960 1,359 1,225 1,312 1,537 1,454 1,314 6,679
31 South Africa CD:NGI Aerial 17 36 99 705 889 735 681 796 661 477 666 833 720 865 6,441
32 OpenRailwayMap 0 0 0 0 0 6 17 798 1,101 851 852 936 1,007 935 5,877
33 Japan GSI 0 0 27 709 513 850 1,162 589 817 754 864 965 885 653 5,750
34 SPW(allonie) most recent aerial imagery 0 0 0 0 0 725 969 945 1,133 1,097 1,201 1,131 1,171 1,140 5,645
35 AIV Flanders 0 0 0 0 0 0 1,148 1,664 1,909 1,859 1,204 6 0 0 5,226
36 Baden-WĂĽrttemberg DOP20 0 0 0 0 0 0 0 0 0 0 0 0 1,954 3,773 4,780
37 NC OneMap Latest Orthoimagery (Natural Color) 0 0 0 0 0 0 0 0 2 802 1,247 1,148 1,524 1,621 4,757
38 CyclOSM 0 0 0 0 0 0 0 20 515 2,746 1,795 58 30 23 4,698
39 LINZ NZ Aerial Imagery 0 0 0 0 324 693 665 603 796 697 836 825 941 852 4,665
40 Czechia CUZK orthophoto 0 0 0 0 0 0 0 0 0 0 0 1,909 2,337 2,029 4,629
41 KartaView Images 0 0 0 0 0 0 0 0 2 1,195 1,370 1,317 1,489 1,230 4,549
42 Cadastre 0 0 0 0 0 821 1,043 1,175 1,087 994 1,000 978 992 750 4,535
43 SDFE aerial imagery 0 0 0 0 574 1,052 937 753 1,154 1,119 758 10 0 0 4,343
44 Digitaal Vlaanderen most recent aerial imagery 0 0 0 0 0 0 0 0 0 0 974 1,673 1,935 1,663 4,335
45 Orthophotos of mainland Portugal - 25cm - 2018 (DGT) 0 0 0 0 0 0 0 0 668 1,466 1,514 1,822 282 9 4,283
46 Stamen Terrain 0 0 0 120 556 749 661 565 696 629 380 44 26 13 4,116
47 PDOK Luchtfoto Beeldmateriaal 25cm 0 0 0 0 2,632 1,901 1 0 0 0 0 0 0 0 3,924
48 Ortofotomozaika SR 0 0 0 0 0 0 0 50 565 957 1,203 1,097 1,186 1,098 3,913
49 Geoportal 2 Nazwy ulic 0 0 0 0 0 0 0 0 3,484 144 131 136 142 147 3,713
50 NRW Liegenschaftskataster 0 0 0 0 0 0 0 0 0 0 2,179 1,858 257 257 3,580
51 MD Latest 6 Inch Aerial Imagery 0 0 0 0 25 38 63 54 311 932 828 783 893 838 3,461
52 Belgium AGIV Orthophoto Flanders 0 0 107 1,493 1,722 1,037 3 0 1 0 1 0 0 0 3,433
53 SDFI 0 0 0 0 0 0 0 0 0 0 523 1,243 1,445 1,290 3,308
54 LPI NSW Imagery 0 0 5 202 1,005 1,120 996 757 4 2 0 0 0 0 3,216
55 OpenAerialMap Mosaic, by Kontur.io 0 0 0 0 0 0 0 0 0 127 1,017 862 922 674 3,187
56 Niedersachsen DOP20 0 0 0 0 0 0 0 0 0 0 0 0 850 2,773 3,154
57 GeoScribble latest notes 0 0 0 0 0 0 0 0 0 0 0 605 1,451 1,334 3,103
58 National Agriculture Imagery Program 0 0 0 0 0 0 240 644 670 507 654 791 718 350 3,041
59 Mapilio Images 0 0 0 0 0 0 0 0 0 0 548 1,097 1,326 1,113 3,009
60 USGS Topographic Maps 68 140 216 230 278 394 383 418 547 536 531 522 512 387 2,957
61 Brandenburg GeoBasis-DE/LGB (latest) / DOP20c 0 0 0 0 0 0 0 0 0 0 252 1,247 1,427 1,184 2,946
62 Thunderforest Landscape 0 0 0 1,132 1,323 683 18 12 3 1 2 2 2 4 2,913
63 TIGER Roads 111 26 0 0 119 447 739 736 845 420 320 296 350 268 2,731
64 Panoramax Images 0 0 0 0 0 0 0 0 0 0 0 597 1,537 1,416 2,728
65 GURS: Slovenia orthophoto 25cm (DOF025) 0 0 0 0 0 0 0 156 587 631 669 701 744 680 2,669
66 .kml data file 0 0 0 0 53 413 438 559 387 366 454 467 530 370 2,663
67 © GeoBasis-DE/LVermGeo LSA, DOP20 0 0 0 0 0 0 0 0 133 380 883 914 931 806 2,661
68 Berlin aerial photography 0 0 0 0 0 762 1,215 950 367 0 0 0 0 0 2,544
69 Orthophotos of mainland Portugal - 25 cm - 2018 (DGT) 0 0 0 0 0 0 0 0 0 0 0 0 1,506 1,389 2,501
70 Lithuania - NŽT ORT10LT 0 81 79 96 294 345 310 285 365 377 424 424 455 402 2,480
71 MassGIS 2021 Aerial Imagery 0 0 0 0 0 0 0 0 0 1,065 1,169 789 47 44 2,351
72 Helsinki region orthophoto 0 0 0 0 0 47 445 383 457 510 493 504 567 512 2,337
73 dgu.hr: Croatia 2019-2020 aerial imagery 0 0 0 0 0 0 0 0 447 887 887 798 37 22 2,290
74 StratMap CapArea, Brazos & Kerr Imagery (Natural Color 2021) 0 0 0 0 0 0 0 0 136 510 499 412 564 543 2,097
75 NRW vDOP 0 0 0 0 0 0 0 0 1 0 121 1,973 105 79 2,079
76 MassGIS 2023 Aerial Imagery 0 0 0 0 0 0 0 0 0 0 0 593 1,396 626 2,050
77 GRAFCAN OrtoExpress Urbana - Canary Islands 0 0 0 0 0 0 16 235 348 431 427 447 476 413 2,037
78 HOTOSM 0 0 0 0 2,028 1 0 0 0 0 0 0 0 0 2,028
79 Strava 4 290 378 546 571 318 112 110 76 53 133 283 292 31 1,997
80 ICGC - Ortofoto de Catalunya 1:2.500 vigent 0 0 0 0 0 0 0 0 0 0 966 1,200 305 2 1,978
81 Catastro Spain 0 0 0 0 0 220 337 360 383 366 374 447 473 402 1,858
82 ICGC - Ortofoto de Catalunya 0 0 0 0 0 0 0 0 0 0 0 0 1,052 1,124 1,857
83 Rheinland-Pfalz DOP20 0 0 0 0 0 0 0 0 0 0 0 0 454 1,581 1,795
84 DCS NSW Imagery 0 0 0 0 0 0 0 305 982 585 189 175 172 175 1,746
85 OpenStreetMap 1,708 1 1 0 4 7 0 0 1 2 4 5 2 1 1,730
86 Kanton Zurich, Orthofoto ZH FrĂĽhjahr 2021 RGB 5cm 0 0 0 0 0 0 0 0 0 431 692 771 529 24 1,708
87 ThĂĽringen DOP20 0 0 0 0 0 0 0 0 0 0 0 318 1,049 855 1,699
88 AGIV Flanders most recent aerial imagery 0 0 0 0 0 1,129 879 1 0 0 0 0 0 0 1,699
89 Orthophoto (2016–2018), 1:5000, Latvia 0 0 0 0 0 0 0 0 373 419 437 447 494 383 1,670
90 MassGIS 2019 Orthos 0 0 0 0 0 0 0 278 1,356 268 80 25 1 0 1,657
91 OS OpenData StreetView 58 192 208 214 285 310 443 451 302 0 0 0 0 0 1,649
92 Latest available ortho geoportail.lu 0 0 0 0 0 243 262 231 281 302 305 298 347 314 1,645
93 PNOA-Spain-TMS 0 0 0 0 0 0 0 1,573 29 0 0 0 0 0 1,596
94 Berlin/Geoportal DOP20RGB (2024) 0 0 0 0 0 0 0 0 0 0 0 0 868 905 1,519
95 Actueel_ortho25_WMS 0 0 0 0 0 0 0 1,462 58 0 0 0 0 0 1,489
96 FR-Cadastre 0 0 40 681 842 281 1 1 3 0 0 0 0 0 1,439
97 Berlin/Geoportal DOP20RGBI (2022) 0 0 0 0 0 0 0 0 0 475 1,159 16 5 0 1,421
98 ITACyL - Castilla y LeĂłn 0 0 0 0 0 0 0 0 0 0 149 541 622 561 1,353
99 Berlin/Geoportal DOP20RGBI (2024) 0 0 0 0 0 0 0 0 0 0 0 615 959 8 1,351
100 South Tyrol Orthofoto 2020 0 0 0 0 0 0 0 0 225 548 575 412 25 14 1,350