Sorted Open Data with Shapely and SVG

Posted on

I recently came across the Sorted Cities project by Hans Hack. He uses building footprint data extracted from OpenStreetMap to create beautiful posters, showing all of the buildings in a city sorted by their area.

As soon as I saw this, the hacker in me thought “how would I go about creating this myself?”

Shapely has a fantastic feature that converts geometries into an SVG representation, which is used to display the geometry in Jupyter. For example:

from shapely.geometry import Point
from shapely.ops import cascaded_union
cascaded_union([Point(0,0).buffer(2), Point(2,0).buffer(2)])

The method that does this, BaseGeometry._repr_svg_, needs a little tweaking to ensure all of the geometries are displayed at the same scale, rather than with a defined height. But the hard work is already done. We just need to sort the geometries and rely on HTML for the layout.

GeoPandas makes quick work of loading data from a shapefile and sorting by area:

df = GeoDataFrame.from_file(filename)
df["AREA"] = df["geometry"].area
df = df.sort_values(by="AREA", ascending=False)

I considered using the same OSM building dataset, but instead decided to try something novel. I work as a hydrologist and spend a lot of time using data related to rivers, lakes and reservoirs. Lakes and reservoirs seemed an obvious choice as they have such distinctive shapes. Fortunately the Environment Agency produce some open data for this: WFD Lake Waterbodies.

The result is shown below. Hovering over a lake will show the name and calculated area.

Windermere (14.36 km²) Rutland Water (12.12 km²) Kielder Water (10.29 km²) Ullswater (8.68 km²) Grafham Water (6.32 km²) Derwent Water (5.29 km²) Bassenthwaite Lake (5.24 km²) Llyn Trawsfynydd (4.93 km²) Abberton Reservoir (4.74 km²) Coniston Water (4.71 km²) Chew Valley lake (4.65 km²) Lake Vyrnwy / Llyn Efyrnwy (4.53 km²) Llyn Tegid or Bala Lake (4.15 km²) Derwent Reservoir (3.90 km²) Haweswater Reservoir (3.79 km²) Colliford Lake (3.79 km²) Hanningfield Reservoir (3.62 km²) Llyn Brenig (3.62 km²) Llyn Celyn (3.24 km²) Thirlmere (3.13 km²) Blithfield Reservoir (3.12 km²) Bewl Water (3.09 km²) Llyn Alaw (3.08 km²) Pitsford Water (3.03 km²) Ennerdale Water (3.01 km²) Cow Green Reservoir (2.94 km²) Roadford Lake (2.92 km²) Queen Mary Reservoir (2.88 km²) Carsington Water (2.85 km²) Wast Water (2.78 km²) Claerwen Reservoir (2.65 km²) Crummock Water (2.50 km²) Draycote Water (2.44 km²) Llyn Clywedog (2.43 km²) Nant-y-moch Reservoir (2.12 km²) Cardiff Bay (2.11 km²) Caban-coch Reservoir (2.10 km²) Llyn Brianne Reservoir (2.03 km²) Ladybower Reservoir (2.00 km²) The Queen Mother Reservoir (1.91 km²) Wraysbury Reservoir (1.83 km²) Llandegfedd Reservoir (1.76 km²) Blagdon Lake (1.65 km²) Alton Water Reservoir (1.64 km²) King Georges Reservoir (1.63 km²) Rollesby Broad or Ormesby Broad (1.59 km²) Eyebrook Reservoir (1.55 km²) Hickling Broad (1.54 km²) Wimbleball Lake (1.53 km²) Alwen Reservoir (1.51 km²) Farmoor Reservoir (1.49 km²) Grimwith Reservoir (1.43 km²) Llangorse Lake (1.40 km²) King George VI Reservoir (1.38 km²) Hornsea Mere (1.33 km²) Stocks Reservoir (1.30 km²) Talybont Reservoir (1.30 km²) William Girling Reservoir (1.29 km²) Gouthwaite Reservoir (1.26 km²) Queen Elizabeth 2 Storage Reservoir (1.22 km²) Balderhead Reservoir (1.14 km²) Selset Reservoir (1.14 km²) Usk Reservoir (1.11 km²) Weir Wood Reservoir (1.07 km²) Rivington Reservoirs (1.06 km²) Bough Beech Reservoir (1.06 km²) Stithians Reservoir (1.05 km²) Audenshaw Reservoirs (1.04 km²) Hallington Reservoirs or West Hallington Reservoir (1.04 km²) Llyn Cowlyd Reservoir (1.04 km²) Staines Reservoirs (1.00 km²) Catcleugh Reservoir (0.99 km²) Llyn Padarn (0.98 km²) Pontsticill Reservoir (0.97 km²) Esthwaite Water (0.96 km²) Manor, Fleet, Abbey, St Ann's Lakes atThorpe Park (0.95 km²) Cheddar Reservoir (0.92 km²) Craig Goch Reservoir (0.91 km²) Buttermere (0.91 km²) Eglwys Nunydd Reservoir (0.91 km²) Llyn Cwellyn (0.90 km²) Foremark Reservoir (0.88 km²) Attenborough NR (0.87 km²) Covenham Reservoir (0.86 km²) Ameys Lake or Theale Lakes (0.86 km²) Chasewater (0.82 km²) Ogston Reservoir (0.80 km²) Staunton Harold Reservoir (0.79 km²) Colt Crag Reservoir (0.78 km²) Stewartby Lake (0.78 km²) Ardingly Reservoir (0.78 km²) Eccup Reservoir (0.77 km²) Aqualate Mere (0.75 km²) Llys-y-fr¿n Reservoir (0.74 km²) Tittesworth Reservoir (0.74 km²) Anglezarke Reservoir (0.72 km²) Staines Reservoirs (0.70 km²) Swithland Reservoir (0.69 km²) Cefni Reservoir (0.68 km²) Derwent Reservoir (0.67 km²) Slapton Ley (0.66 km²) Swinsty Reservoir (0.66 km²) Belvide Reservoir (0.65 km²) Pennington Flash (0.65 km²) Killington Reservoir (0.64 km²) Scar House Reservoir (0.63 km²) Farnham Flint or Englefield Lagoon (0.63 km²) Rudyard Reservoir (0.62 km²) Howden Reservoir (0.62 km²) Bakethin (0.61 km²) Fewston Reservoir (0.61 km²) Grasmere (0.61 km²) Loweswater (0.60 km²) Malham Tarn (0.60 km²) Thrapston Lake (0.60 km²) Fritton Decoy (0.60 km²) Brogborough Reservoir (0.59 km²) Great Lake or Welbeck (0.59 km²) Darwell Reservoir (0.58 km²) Attenborough NR (0.58 km²) Grassholme Reservoir (0.58 km²) Barton Broad (0.58 km²) Thruscross Reservoir (0.57 km²) Burrator Reservoir (0.56 km²) Tallington Lakes (0.56 km²) Sonning Eye gravel pit (0.56 km²) Torside Reservoir (0.55 km²) Stanford Reservoir (0.55 km²) The Loe (0.54 km²) Llwyn-on Reservoir (0.54 km²) Sutton Bingham Reservoir (0.54 km²) Wraysbury II Gravel Pit/ Wellapool Lake (0.53 km²) Cropston Reservoir (0.53 km²) Virginia Water (0.53 km²) Ibsley Water (0.52 km²) Combermere (0.52 km²) Crowdy Reservoir (0.52 km²) Winscar Reservoir (0.51 km²) Llyn Eigiau Reservoir (0.51 km²) Ardleigh Reservoir (0.51 km²) Tal-y-llyn Lake (0.51 km²) Siblyback Lake (0.51 km²) Hollowell Reservoir (0.50 km²) Langsett Reservoir (0.49 km²) White Holme Reservoir (0.49 km²) Clatworthy Reservoir (0.49 km²) Rostherne Mere (0.48 km²) Arlington Reservoir (0.48 km²) Murston Lakes, nature reserve (0.48 km²) Island Barn Reservoir (0.48 km²) Broomhead Reservoir (0.47 km²) Llyn Llydaw (0.47 km²) Greenlee Lough (0.46 km²) Sweethope Loughs (0.46 km²) Ravensthorpe Reservoir (0.46 km²) Penygarreg Reservoir (0.46 km²) Hury Reservoir (0.45 km²) Wayoh Reservoir (0.44 km²) Woodhead Reservoir (0.44 km²) Burrows Pit (0.44 km²) Nook Beach (0.44 km²) Valehouse Reservoir (0.43 km²) Westbere Lakes (0.42 km²) Burnhope Reservoir (0.41 km²) Llyn Conwy (0.41 km²) Scaling Dam Reservoir (0.41 km²) Wraysbury No1 Gravel Pit (0.40 km²) Scammonden Water (0.39 km²) Llyn Ogwen (0.39 km²) Pentwyn Reservoir (0.38 km²) Turton and Entwistle Reservoir (0.38 km²) Blackmoorfoot Reservoir (0.38 km²) Chelmarsh Reservoir (0.38 km²) Hoveton Great Broad (0.37 km²) Llyn Peris (0.37 km²) Ranworth Broad (0.37 km²) Leighton Reservoir (0.37 km²) Cray Reservoir (0.36 km²) Widdop Reservoir (0.36 km²) Dungeness Gravel Pit (0.36 km²) Shustoke Reservoirs (0.36 km²) Angram Reservoir (0.35 km²) Llyn Arenig fawr (0.35 km²) Fontburn Reservoir (0.35 km²) Devoke Water (0.34 km²) Mid Colne Valley (0.34 km²) Tatton Mere (0.34 km²) Lily Ponds or Bosherston Lake (includes Central, Lower and Upper) (0.34 km²) Durleigh Reservoir (0.33 km²) Horsey Mere (0.33 km²) Watergrove Reservoir (0.33 km²) Blackbrook Reservoir (0.32 km²) Llyn Cwmystradllyn (0.32 km²) Upper Tamar Lake (0.32 km²) Wroxham Broad (0.32 km²) Great Puckstone (0.32 km²) Fernilee Reservoir (0.31 km²) Little Sea (0.31 km²) Dovestone Reservoir (0.31 km²) Errwood Reservoir (0.31 km²) Delph Reservoir (0.31 km²) Fernworthy Reservoir (0.31 km²) Coombe Pool (0.31 km²) Thoresby Lake (0.30 km²) Clumber Lake (0.30 km²) Church Wilne Reservoir (0.30 km²) Kenfig Pool (0.29 km²) Frensham Great Pond (0.29 km²) Seventy Acres (0.29 km²) Semer Water (0.29 km²) Llyn Coron (0.28 km²) Banbury Reservoir (0.28 km²) Broomlee Lough (0.28 km²) Warley Moor Reservoir (0.28 km²) Warland Reservoir (0.28 km²) Reservoir No 4 (0.28 km²) Clowbridge Reservoir (0.28 km²) Bessborough Reservoir (0.27 km²) Cole Mere (0.27 km²) Castle Water (0.27 km²) Lockwood Reservoir (0.27 km²) Fleet Pond (0.26 km²) Rockland Broad (0.26 km²) Drift Reservoir (0.25 km²) Blackton Reservoir (0.25 km²) Tindale Tarn (0.25 km²) Tabley Mere (0.25 km²) Blackstone Edge Reservoir (0.25 km²) Ystradfellte Reservoir (0.24 km²) Llyn Teifi (0.24 km²) Waskerley Reservoir (0.24 km²) Withens Clough Reservoir (0.24 km²) Agden Reservoir (0.24 km²) Ardsley Reservoir (0.24 km²) Coate Water (0.24 km²) Burnmoor Tarn (0.24 km²) Smiddy Shaw Reservoir (0.24 km²) Baitings Reservoir (0.24 km²) Powdermill Reservoir (0.24 km²) White Mere (0.24 km²) Tunstall Reservoir (0.23 km²) Dale Dike Reservoir (0.23 km²) Barrow Reservoir (0.23 km²) Yarrow Reservoir (0.23 km²) Llyn Gynon (0.23 km²) Great Pool or Westwood Great Pool (0.23 km²) Upper Neuadd Reservoir (0.23 km²) Greenbooth Reservoir (0.22 km²) Upper Barden Reservoir (0.22 km²) Ogden Reservoir (0.22 km²) Ashworth Moor Reservoir (0.22 km²) Hardley Flood (0.22 km²) Burton Mill Pond (0.22 km²) Fishmoor Reservoir (0.22 km²) Rockford Lake (0.22 km²) Lower Barden Reservoir (0.22 km²) Strines Reservoir (0.21 km²) Jumbles Reservoir (0.21 km²) Chelker Reservoir (0.21 km²) Foxcote Reservoir (0.21 km²) Meldon Reservoir (0.21 km²) Beacons Reservoir (0.21 km²) Kennick Reservoir (0.21 km²) Booth Wood Reservoir (0.21 km²) Ingbirchworth Reservoir (0.21 km²) Roundhill Reservoir (0.20 km²) Rhodeswood Reservoir (0.20 km²) Frampton Gravel Pits (0.20 km²) Over Water (0.20 km²) Midhope Reservoir (0.20 km²) Greatstone Lake (0.19 km²) Blaen-y-cwm Reservoir (0.19 km²) Brothers Water (0.19 km²) Knight Reservoir (0.19 km²) Gorple Reservoirs (upper) (0.19 km²) Green Withens Reservoir (0.19 km²) Langtoft Gravel Pits (0.19 km²) Gorple Reservoirs (lower) (0.19 km²) Oakmere (0.18 km²) Hanmer Mere (0.18 km²) Blashford Lake (0.18 km²) Elter Water or Elterwater (0.18 km²) Castle Carrock Reservoir (0.18 km²) Argal Reservoir (0.18 km²) North Metropolitan pit (0.18 km²) Wistlandpound Reservoir (0.18 km²) Ringstone Edge Reservoir (0.18 km²) Dean Clough Reservoir (0.17 km²) Thornton Moor Reservoir (0.17 km²) Cop Mere or Cop Mere (0.17 km²) Cantref Reservoir (0.17 km²) Digley Reservoir (0.17 km²) Lamaload Reservoir (0.17 km²) Hedgecourt Lake (0.17 km²) Blea Water (0.17 km²) North Point Lake, Rye golf club (0.17 km²) Arnfield Reservoir (0.17 km²) Llyn Bodlyn (0.17 km²) Ffynnon Llugwy Reservoir (0.16 km²) Chew Reservoir (0.16 km²) Harlock Reservoir (0.16 km²) Hayeswater (0.16 km²) Sowley Pond (0.16 km²) unnamed (0.16 km²) Wentwood Reservoir (0.16 km²) The Mere (0.16 km²) Ivy Lake (0.16 km²) Lower Tamar Lake (0.16 km²) Piethorne Reservoir (0.16 km²) College Reservoir (0.16 km²) Martham Broad or Martham Broad (North and South) (0.16 km²) Kempton Park Reservoir(E) Dis used ? (0.16 km²) Hurworth Burn Reservoir (0.16 km²) Cotswold Water Park Lake 12 (0.16 km²) Stodmarsh Nature Reserve Pool (0.15 km²) Kinder Reservoir (0.15 km²) Avon Dam Reservoir (0.15 km²) Crose Mere (0.15 km²) Great Southern Reservoir (0.15 km²) Dozmary Pool (0.15 km²) Cant Clough Reservoir (0.15 km²) Walthamstow Reservoirs (0.15 km²) Sherborne Lake (0.15 km²) Cowm Reservoir (0.15 km²) Reservoir no 1 (0.15 km²) Dulyn Reservoir (0.15 km²) Deer Hill Reservoir (0.15 km²) Frensham Little Pond (0.15 km²) unnamed (0.15 km²) Swanholme Lakes (0.15 km²) Crookfoot Reservoir (0.15 km²) Butterley Reservoir (0.15 km²) Fordwich Lakes (0.14 km²) Upper Lliw Reservoir (0.14 km²) Ogden Water (0.14 km²) Walshaw Dean Reservoirs (middle) (0.14 km²) Tarn Hows (0.14 km²) Beaver Dyke Reservoir (0.14 km²) Llyn Egnant (0.14 km²) Groby Pool (0.14 km²) Royd Moor Reservoir (0.14 km²) Great Northern Reservoir (0.14 km²) Levers Water (0.14 km²) Marchlyn Mawr Reservoir (0.14 km²) Llyn Cwm Bychan (0.14 km²) Llyn Arenig Fach (0.14 km²) Warwick Reservoir (0.14 km²) Llyn Cau (0.13 km²) Llyn Cwm Dulyn (0.13 km²) Lockwood Beck Reservoir (0.13 km²) Toddbrook Reservoir (0.13 km²) Cwm Llied Reservoir (0.13 km²) Llyn Idwal (0.13 km²) Worthington Reservoir (0.13 km²) Lower Lliw Reservoir (0.13 km²) Bowyers Water (0.13 km²) Clough Bottom Reservoir (0.13 km²) Rivelin Dams (0.13 km²) The Lake (0.13 km²) Llyn Bran (0.13 km²) Tottiford Reservoir (0.13 km²) Hurstwood Reservoir (0.13 km²) Rosebush Reservoir (0.12 km²) Grizedale Lea Reservoir (0.12 km²) Upper Lliedi Reservoir (0.12 km²) Upper and Lower Castleshaw Reservoirs (0.12 km²) Alston No 2 Reservoir (0.12 km²) Langthwaite Reservoir (0.12 km²) Llyn Fyrddon Fawr (0.12 km²) Llyn Tecwyn Uchaf (0.12 km²) Alston No 1 Reservoir (0.12 km²) Lower Laithe Reservoir (0.12 km²) Ogden Reservoir (0.12 km²) Grwyne Fawr Reservoir (0.12 km²) Venford Reservoir (0.11 km²) Lluest-wen Reservoir (0.11 km²) Cadney Reservoir (0.11 km²) Petty Pool (0.11 km²) Walshaw Dean Reservoirs (lower) (0.11 km²) Llyn Morwynion (0.11 km²) unnamed (0.11 km²) Warwick Reservoir (0.11 km²) Murston Lakes, angling lakes (0.11 km²) Trenchford Reservoir (0.11 km²) Llyn Llygeirian (0.11 km²) Embsay Reservoir (0.11 km²) Big Water Reservoir (0.11 km²) Grisedale Tarn (0.11 km²) Marton Pool or Marton Pool, Chirbury (0.11 km²) Mockbeggar Lake (0.11 km²) Water Sheddles Reservoir (0.11 km²) Lumley Moor Reservoir (0.11 km²) Barmby (0.11 km²) Llyn Cwmffynnon (0.11 km²) Scargill Reservoir (0.11 km²) Walkerwood Reservoir (0.11 km²) Walshaw Dean Reservoir (upper) (0.11 km²) Poaka Beck Reservoir (0.11 km²) Blelham Tarn (0.11 km²) Oss Mere (0.11 km²) Llynnau Gamallt (0.10 km²) Redmires Reservoirs (0.10 km²) Llyn Craigypistyll or Llyn Craig y Pistyll (0.10 km²) Stanford Water (0.10 km²) Bar Mere (0.10 km²) Trimpley Reservoir (0.10 km²) Fordwhich lake East (0.10 km²) Ponden Reservoir (0.10 km²) Barnacre Reservoirs (0.10 km²) Crag Lough (0.10 km²) Bomere Pool (0.10 km²) Llyn Eiddew-mawr (0.10 km²) Llyn Fawr (0.10 km²) Brownhill Reservoir (0.10 km²) Cod Beck Reservoir (0.10 km²) Bolam Lake (0.10 km²) Llyn Dinam (0.10 km²) Roddlesworth Reservoirs (0.10 km²) Ridgegate Reservoir (0.10 km²) Spring Mill Reservoir (0.10 km²) Plas Uchaf and Dolwen Reservoirs (0.09 km²) Milford Lake (0.09 km²) Chapel Mere (0.09 km²) Fenemere (0.09 km²) Attenborough NR (0.09 km²) Hawkridge Reservoir (0.09 km²) Yeoman Hey Reservoir (0.09 km²) Springs Reservoir (0.09 km²) Llyn Elsi Reservoir (0.09 km²) Penderyn Reservoir (0.09 km²) Higher Swineshaw Reservoir (0.09 km²) Lound Mill Water (0.09 km²) Forest Mere (0.09 km²) Betley Mere (0.09 km²) Calf Hey Reservoir (0.09 km²) unnamed (0.09 km²) Ogden Reservoir (0.09 km²) Llyn y Garn (0.09 km²) Pennington Reservoir (0.09 km²) Ty Mawr Reservoir (0.09 km²) Holden Wood Reservoir (0.09 km²) Llyn Cynwch (0.09 km²) Rooden Reservoir (0.08 km²) Thurstonfield Lough (0.08 km²) Carno Reservoir (0.08 km²) Hookers Pit (0.08 km²) Sea Mere or Hingham Sea Mere (0.08 km²) Glaslyn (0.08 km²) Broadstone Reservoir (0.08 km²) Cheshunt Lake (0.08 km²) Mixenden Reservoir (0.08 km²) Trentabank Reservoir (0.08 km²) Dowdeswell Reservoir (0.08 km²) Boldermere (0.08 km²) Walthamstow Reservoirs (0.08 km²) Dowry Reservoir (0.08 km²) Brasside Pond (0.08 km²) Old Alresford Pond (0.08 km²) Decoy Broad (0.08 km²) Red Tarn, Helvellyn (0.08 km²) Ryburn Reservoir (0.08 km²) Linacre Reservoirs (0.08 km²) Middle Naden Reservoir (0.08 km²) Linshiels Lake (0.08 km²) Thompson Water (0.08 km²) Pant-yr-eos Reservoir (0.07 km²) Airy Holm Reservoir (0.07 km²) Bracebridge Pool (0.07 km²) Llangynidr Reservoir (0.07 km²) Sprotborough Flash (0.07 km²) Blea Tarn (0.07 km²) Appleton Reservoir (0.07 km²) Bollinhurst Reservoir (0.07 km²) Llyn Tryweryn (0.07 km²) Windleden Reservoirs (Upper) (0.07 km²) Stoke Newington East Reservoir (0.07 km²) Simpson Ground Reservoir (0.07 km²) Edgbaston Pool (0.07 km²) Melynllyn (0.07 km²) Mitchells House Reservoirs (0.07 km²) Llyn Cyfynwy (0.07 km²) Graincliffe Reservoir (0.07 km²) Shon-Sheffreys Reservoir (0.07 km²) Grimsbury Reservoir (0.07 km²) Melchett Mere (0.07 km²) Hatchet Pond (0.07 km²) Snodland Reservoir (0.07 km²) Swinden Reservoirs (0.07 km²) Wessenden Reservoir (0.07 km²) Deanhead Reservoir (0.07 km²) Pendinas Reservoir (0.07 km²) Linbrook Lake (0.07 km²) Upton Broad (0.07 km²) Horse Coppice Reservoir (0.07 km²) Stover Lake (0.07 km²) Reva Reservoir (0.07 km²) Sunnyhurst Hey Reservoir (0.07 km²) Whitton Castle Lakes, Witton le Wear (0.06 km²) Laneshaw Reservoir (0.06 km²) Gormire Lake (0.06 km²) Hisehope Reservoir (0.06 km²) Mickletown Ings (0.06 km²) Llyn Llygad Rheidol (0.06 km²) Snails Lake (0.06 km²) Little Langdale Tarn (0.06 km²) Mytchett Lake (0.06 km²) Long Pit (0.06 km²) Costessey Pit No. 2 (0.06 km²) Titchfield Haven (0.06 km²) Black Moss Reservoirs (0.06 km²) Luxhay Reservoir (0.06 km²) Yateholme Reservoir (0.06 km²) Llyn Cerrigllwydion Isaf (0.06 km²) Betton Pool (0.06 km²) New Years Bridge Reservoir (0.06 km²) Rake Brook Reservoir (0.06 km²) Earnsdale Reservoir (0.06 km²) Roddlesworth Reservoirs (0.06 km²) Leeming Reservoir (0.06 km²) Keighley Moor Reservoir (0.06 km²) Pett Pools (0.06 km²) Dingle Reservoir (0.06 km²) Hawes Water, Silverdale (0.06 km²) Englemere Pond (0.06 km²) Llyn Cwm-mynach or Llyn Cwm Mynach (0.06 km²) Llynnoedd Ieuan (0.06 km²) Churn Clough Reservoir (0.06 km²) Gorpley Reservoir (0.06 km²) Cockshoot Broad (0.06 km²) Hawkins Pond (0.05 km²) Alston Reservoirs (0.05 km²) Ynysyfro Reservoir (0.05 km²) Low Reservoir to Henderson Filters (0.05 km²) Cowpe Reservoir (0.05 km²) Stowe Pool, Lichfield (0.05 km²) Wessend Reservoir (0.05 km²) Fens Top Pool (0.05 km²) Llyn H¿r (0.05 km²) Llyn Hywel (0.05 km²) Llyn Llagi (0.05 km²) Readycon Dean Reservoir (0.05 km²) Windleden Reservoirs (Lower) (0.05 km²) Upper Naden Reservoir (0.05 km²) Grizedale Reservoir (0.05 km²) Nant-y-Ffrith Reservoir (0.05 km²) Clumber Park Lake West (0.05 km²) Harden Reservoir (0.05 km²) Harlow Hill Reservoir (0.05 km²) Ellingham (0.05 km²) Swineshaw Reservoir (0.05 km²) Glemsford pits (0.05 km²) Maer Pool (0.05 km²) Ramsden Clough Reservoir (0.05 km²) Lower Swineshaw Reservoir (0.05 km²) Coldwell Reservoirs (0.05 km²) Hatch Mere (0.05 km²) Kitcliffe Reservoir (0.05 km²) Guide Reservoir (0.04 km²) Heron Lake (0.04 km²) Llyn Gelli Gain (0.04 km²) Lower Neuadd Reservoir (0.04 km²) Scoat Tarn (0.04 km²) Ghyll Head Reservoir (0.04 km²) Stubden Reservoir (0.04 km²) Sibdon Pond Nature Reserve, Blaydon (0.04 km²) Greenfield Reservoir (0.04 km²) L Lake or Rainworth Lake (0.04 km²) Nant-moel Reservoir (0.04 km²) Mitchell's House Reservoir No 2 (0.04 km²) Quoisley Big Mere (0.04 km²) Ramsden Reservoir (0.04 km²) Dean Head Reservoirs (upper) (0.04 km²) Marchlyn Bach Reservoir (0.04 km²) The Tarn (0.04 km²) Melbury Reservoir (0.04 km²) Moor Monkton Storage Reservoir (0.04 km²) Dean Head Reservoirs (lower) (0.04 km²) Walverden Reservoir (0.04 km²) Norman Hill Reservoir (0.04 km²) Bilberry IRE (0.04 km²) Snailsden Reservoir (0.04 km²) Jennetts Reservoir (0.04 km²) Coldwell Reservoirs (0.04 km²) unnamed (0.03 km²) Little Swinburne Reservoir (0.03 km²) Little Mere (0.03 km²) Bottoms Reservoir Macclesfield (0.03 km²) Queensmead (0.03 km²) Felmersham Gravel Pits (0.03 km²) Nutscale Reservoir (0.03 km²) Dubbs Reservoir (0.03 km²) nr. Kelloe (0.03 km²) Hemlington Lake (0.03 km²) Riding Wood Reservoir (0.03 km²) Charlston Pond, Billingham (0.03 km²) Ten Acre Reservoir (0.03 km²) Brushes Reservoir (0.03 km²) unnamed (0.03 km²) Pond y Gwaith (0.03 km²) Monkswood Reservoir (0.03 km²) High Bullough Reservoir (0.03 km²) Adlington Reservoir (0.03 km²) Lower Naden Reservoir (0.03 km²) Crook Gate Reservoir (0.03 km²) Redcar Stell (0.03 km²) Blakeley Reservoir (0.03 km²) Joe's Pond, Rainton Bridge (0.03 km²) Pencaye Top Reservoir (0.02 km²) Weecher Reservoir (0.02 km²) Berrington Pool (0.02 km²) Lake at Haggerston Castle Holiday Park (0.02 km²) Heath Lake (0.02 km²) unnamed (0.02 km²) Ashford Reservoir (0.02 km²) Crag Pond, nr. The Rigg, Lartington (0.02 km²) Ogden Reservoir (0.02 km²) Captains Pond (0.02 km²) Damas Gill (0.02 km²) Lee Green Reservoir (0.02 km²) Bussow Reservoir (0.02 km²) Slade Reservoirs (0.02 km²) Scotch Peters Reservoir (0.02 km²) Cargenwyn Reservoir (0.02 km²) unnamed (0.02 km²) Cairn Mound Reservoir (0.02 km²) Moel Dywyll (0.02 km²) Cranmer Pond (0.02 km²) Leigh Reservoir (0.02 km²) Woolmer Pond (0.02 km²) unnamed (0.02 km²) Tallington Lakes (0.02 km²) Quoisley Little Mere (0.02 km²) Douster Pond (0.02 km²) Black Pond (0.02 km²) Castell Nos Reservoir (0.02 km²) nr. Pitfield Farm, Little Stainton (0.02 km²) Pickup Bank Reservoir (0.02 km²) unnamed (0.02 km²) Gammaton Reservoirs (0.02 km²) Whitton Castle Lakes, Witton le Wear (0.02 km²) Scout Moor Reservoir (0.02 km²) Bay Pond (0.02 km²) Hanging Lees Reservoir (0.02 km²) unnamed (0.02 km²) nr. Tillery Farm, Wingate (0.02 km²) Pencaye Bottom Reservoir (0.02 km²) Lochnaw (0.02 km²) Slade Reservoirs (0.02 km²) unnamed (0.02 km²) Chapelhouse Reservoir (0.02 km²) Chapel Pit NR (0.02 km²) Carr Bottom Reservoir (0.02 km²) Nanthir Reservoir (0.01 km²) Black Heath Pond (0.01 km²) Willow Garth, Croft on Tees (0.01 km²) Gammaton Reservoirs (0.01 km²) Whitmoor Common Pond (0.01 km²) Red Lake Pool (0.01 km²) Bentley Priory (0.01 km²) Sqabmoor Reservoir (0.01 km²) Norbury Meres (0.01 km²) Tattershall Gravel Pits, Mr Windleys Pit 3 (0.01 km²) The Old Dove (0.01 km²) Teggs Nose Reservoir (0.01 km²) Pinkery Pond or Pinkworthy Pond (0.01 km²) St James Reservoir (0.01 km²) Three Dubs Tarn (0.01 km²) nr. Simpasture Playing Field, Newton Aycliffe (0.01 km²) Shomere Pool (0.01 km²) Swinden Reservoir No. 2 (0.01 km²) Priest Pot (0.01 km²) Colony Reservoir (0.01 km²) Pant Glas (0.01 km²) Llyn Bedydd (0.01 km²) Cilcain Reservoir 3 (0.01 km²) Swinley Park Pond (0.01 km²) Misson Line Bank (0.01 km²) Cornbury Park Lakes (0.01 km²) Reservoir No. 2 (0.01 km²) nr. New Buildings Farm, New Marske (0.01 km²) unnamed (0.01 km²) Reservoirs at Middleton St George (0.01 km²) unnamed (0.01 km²) Otterhead Reservoir (0.01 km²) Fish pond, nr. Grey Towers Farm, Nunthorpe (0.01 km²) Sweat Mere (0.01 km²) Lochnaw (0.01 km²) nr. Whitfield Brow, Bollihope (0.01 km²) Tatton Mere South (0.01 km²) Tatton Mere West (0.01 km²) unnamed (0.01 km²) Upware North Pit (0.01 km²) unnamed (0.01 km²) Upper Swineshaw Reservoir (0.01 km²) Stow Cum Quy Fen (0.01 km²) Ugborough Reservoir (0.01 km²) Black Lake (0.01 km²) Reservoirs at Middleton St George (0.01 km²) Epsom Stew Pond (0.01 km²) unnamed (0.01 km²) unnamed (0.01 km²) Pattison South Pond (0.01 km²) Skelsmergh Tarn (0.00 km²) Low Butterby, nr. Croxdale (0.00 km²) unnamed (0.00 km²) Reservoirs at Middleton St George (0.00 km²) unnamed (0.00 km²) Old Buckenham Fen Mere (0.00 km²) Snipe Lane Pond (0.00 km²) Lochnaw (0.00 km²) Black Mere (0.00 km²) nr. Toft Farm, Marske by the Sea (0.00 km²) Morton Pool (0.00 km²) Lakes at Cassop (0.00 km²) Lane Foxes Pond (0.00 km²) unnamed (0.00 km²) unnamed (0.00 km²) Issues, nr. Campion Lane, Hutton Rudby (0.00 km²) unnamed (0.00 km²) unnamed (0.00 km²) unnamed (0.00 km²) Littleworth Ponds (0.00 km²) Cwmsychan Reservoir (0.00 km²) Mawbray Banks (0.00 km²) Marden Meadow Ponds (0.00 km²) unnamed (0.00 km²) Castle Carr Reservoir (0.00 km²) Nanntymailor Reservoir (0.00 km²) unnamed (0.00 km²) New Hartley Ponds (0.00 km²) Issues, nr. Campion Lane, Hutton Rudby (0.00 km²) Hafoty Reservoir (0.00 km²) Reservoirs at Middleton St George (0.00 km²) Hell Kettles, nr. Oxneyfield, Hurworth Place (0.00 km²) nr. Dunsdale Farm, Wilton (0.00 km²) Mill Pond (0.00 km²) Blue Lagoon Kildale (0.00 km²) Elton Pond (0.00 km²) Lyppard Grange (0.00 km²) Sound Common North Pond (0.00 km²) Syderstone Common (0.00 km²) Priors Park Reservoir (0.00 km²) Cockerham (0.00 km²)

Contains public sector information licensed under the Open Government Licence v3.0.

The first attempt produced quite a large file (2.1MB) as it includes all of the detail of the original data. The following steps were taken to reduce the size of the output.

  • Simplify the geometry with a threshold of 25 metres (selected after some trial and error) using geometry.simplify.
  • Translate the geometry so that the lower left corner is at x=1, y=1. This results in smaller number (and therefore less characters) and is acceptable as the coordinates only need to be relative to each other, not those of another geometry. This is a technique borrowed from the encoding of OSM PBF data.
  • Round the coordinates to the nearest 1 metre.
  • Strip the redundant decimal from the output of Shapely’s SVG representation.
  • Style the paths using a class in CSS rather than inline.

These changes bring the final size down to a more acceptable 388KB (102KB gzipped).

The full source code is given below. It could certainly be more elegant, but I was trying for the least effort (a true hack!).

from geopandas import GeoDataFrame
from shapely.ops import transform
from shapely.affinity import translate
import re

def repr_svg(geometry, base_height=200, scale=None, title=None):
    """SVG representation of a Shapely geometry
    adapted from Shapely's BaseGeometry._repr_svg_ method
    https://github.com/Toblerity/Shapely/blob/master/shapely/geometry/base.py
    """
    svg_attrs = {}

    xmin, ymin, xmax, ymax = geometry.bounds
    expand = 0.04
    widest_part = max([xmax - xmin, ymax - ymin])
    expand_amount = widest_part * expand
    xmin -= expand_amount
    ymin -= expand_amount
    xmax += expand_amount
    ymax += expand_amount
    dx = xmax - xmin
    dy = ymax - ymin

    if scale is None:
        # scale to a given height
        height = base_height
        width = (dx / dy) * base_height
        scale = height / dy
    else:
        # use previously calculated scale factor
        height = dy * scale
        width = dx * scale

    try:
        scale_factor = max([dx, dy]) / max([width, height])
    except ZeroDivisionError:
        scale_factor = 1.0

    view_box = "{xmin} {ymin} {dx} {dy}".format(xmin=xmin, ymin=ymin, dx=dx, dy=dy)
    transform = "matrix(1,0,0,-1,0,{0})".format(ymax + ymin)

    svg_attrs.update({
        "width": width,
        "height": height,
        "viewBox": view_box,
        "preserveAspectRatio": "xMinYMin meet",
    })
    g = """<g transform="{t}">{x}</g>""".format(t=transform, x=geometry.svg(scale_factor))

    # title for image (shown on mouseover)
    if title:
        t = '<title>{t}</title>'.format(t=title)
    else:
        t = ''
    # create svg object
    svg = "<svg {a}>{t}{g}</svg>".format(a=" ".join(["{k}=\"{v}\"".format(k=k,v=v) for k,v in svg_attrs.items()]), t=t, g=g)
    # change style
    svg = re.sub('fill=.*? opacity="0.6"', 'class="lake" stroke-width="{}"'.format(1*scale_factor), svg)
    # remove redundant decimals
    svg = re.sub(' ([0-9]{1,})\.0,([0-9]{1,}).0', r' \g<1>,\g<2>', svg)

    return svg, scale

def round_coord(x, y, z=None):
    """Transform function to round coordinates"""
    x = round(x, 0)
    y = round(y, 0)
    return tuple(filter(None, [x, y, z]))

def main():
    # load and sort the data
    filename = "WFD - Lake Waterbodies.shp"
    df = GeoDataFrame.from_file(filename)
    df["AREA"] = df["geometry"].area
    df = df.sort_values(by="AREA", ascending=False)

    print('<div style="margin-bottom: 30px;">')
    print('<style>.lake {fill: #75CFF0; stroke: #000; stroke-opacity: 0.4;}</style>')

    scale = None
    for r, feature in df.iterrows():
        geometry = feature["geometry"]
        # simplify the geometry
        geometry = geometry.simplify(25)
        xmin, ymin, xmax, ymax = geometry.bounds
        geometry = translate(geometry, xoff=-xmin+1, yoff=-ymin+1)
        geometry = transform(round_coord, geometry)
        # render
        title = "{} ({:.2f} km&#178;)".format(feature["name"], feature["AREA"]/1000**2)
        svg, _scale = repr_svg(geometry, scale=scale, title=title)
        if scale is None:
            scale = _scale
        print(svg)

    print("</div>")
    print("<p>Contains public sector information licensed under the <a href=\"https://www.nationalarchives.gov.uk/doc/open-government-licence/version/3/\">Open Government Licence v3.0.</a></p>")

if __name__ == "__main__":
    main()

License

The repr_svg function is based on the BaseGeometry._repr_svg method is Shapely and is licensed under the simplified BSD license, as below:

Copyright (c) 2007, Sean C. Gillies All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

  • Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
  • Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
  • Neither the name of Sean C. Gillies nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.