-
-
Notifications
You must be signed in to change notification settings - Fork 69
/
Copy path04-geometry-operations.html
2922 lines (2860 loc) · 246 KB
/
04-geometry-operations.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en"><head>
<meta charset="utf-8">
<meta name="generator" content="quarto-1.5.54">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">
<meta name="description" content="An introductory resource for working with geographic data in Python">
<title>4 Geometry operations – Geocomputation with Python</title>
<style>
code{white-space: pre-wrap;}
span.smallcaps{font-variant: small-caps;}
div.columns{display: flex; gap: min(4vw, 1.5em);}
div.column{flex: auto; overflow-x: auto;}
div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
ul.task-list{list-style: none;}
ul.task-list li input[type="checkbox"] {
width: 0.8em;
margin: 0 0.8em 0.2em -1em; /* quarto-specific, see https://github.com/quarto-dev/quarto-cli/issues/4556 */
vertical-align: middle;
}
/* CSS for syntax highlighting */
pre > code.sourceCode { white-space: pre; position: relative; }
pre > code.sourceCode > span { line-height: 1.25; }
pre > code.sourceCode > span:empty { height: 1.2em; }
.sourceCode { overflow: visible; }
code.sourceCode > span { color: inherit; text-decoration: inherit; }
div.sourceCode { margin: 1em 0; }
pre.sourceCode { margin: 0; }
@media screen {
div.sourceCode { overflow: auto; }
}
@media print {
pre > code.sourceCode { white-space: pre-wrap; }
pre > code.sourceCode > span { display: inline-block; text-indent: -5em; padding-left: 5em; }
}
pre.numberSource code
{ counter-reset: source-line 0; }
pre.numberSource code > span
{ position: relative; left: -4em; counter-increment: source-line; }
pre.numberSource code > span > a:first-child::before
{ content: counter(source-line);
position: relative; left: -1em; text-align: right; vertical-align: baseline;
border: none; display: inline-block;
-webkit-touch-callout: none; -webkit-user-select: none;
-khtml-user-select: none; -moz-user-select: none;
-ms-user-select: none; user-select: none;
padding: 0 4px; width: 4em;
}
pre.numberSource { margin-left: 3em; padding-left: 4px; }
div.sourceCode
{ }
@media screen {
pre > code.sourceCode > span > a:first-child::before { text-decoration: underline; }
}
/* CSS for citations */
div.csl-bib-body { }
div.csl-entry {
clear: both;
margin-bottom: 0em;
}
.hanging-indent div.csl-entry {
margin-left:2em;
text-indent:-2em;
}
div.csl-left-margin {
min-width:2em;
float:left;
}
div.csl-right-inline {
margin-left:2em;
padding-left:1em;
}
div.csl-indent {
margin-left: 2em;
}</style>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js" integrity="sha512-bLT0Qm9VnAYZDflyKcBaQ2gg0hSYNQrJ8RilYldYQ1FxQYoCLtUjuuRuZo+fjqhx/qtq/1itJ0C2ejDxltZVFg==" crossorigin="anonymous"></script><script src="site_libs/quarto-nav/quarto-nav.js"></script>
<script src="site_libs/quarto-nav/headroom.min.js"></script>
<script src="site_libs/clipboard/clipboard.min.js"></script>
<script src="site_libs/quarto-search/autocomplete.umd.js"></script>
<script src="site_libs/quarto-search/fuse.min.js"></script>
<script src="site_libs/quarto-search/quarto-search.js"></script>
<meta name="quarto:offset" content="./">
<link href="./05-raster-vector.html" rel="next">
<link href="./03-spatial-operations.html" rel="prev">
<link href="./favicon-32x32.png" rel="icon" type="image/png">
<script src="site_libs/quarto-html/quarto.js"></script>
<script src="site_libs/quarto-html/popper.min.js"></script>
<script src="site_libs/quarto-html/tippy.umd.min.js"></script>
<script src="site_libs/quarto-html/anchor.min.js"></script>
<link href="site_libs/quarto-html/tippy.css" rel="stylesheet">
<link href="site_libs/quarto-html/quarto-syntax-highlighting.css" rel="stylesheet" id="quarto-text-highlighting-styles">
<script src="site_libs/bootstrap/bootstrap.min.js"></script>
<link href="site_libs/bootstrap/bootstrap-icons.css" rel="stylesheet">
<link href="site_libs/bootstrap/bootstrap.min.css" rel="stylesheet" id="quarto-bootstrap" data-mode="light">
<script id="quarto-search-options" type="application/json">{
"location": "sidebar",
"copy-button": false,
"collapse-after": 3,
"panel-placement": "start",
"type": "textbox",
"limit": 50,
"keyboard-shortcut": [
"f",
"/",
"s"
],
"show-item-context": false,
"language": {
"search-no-results-text": "No results",
"search-matching-documents-text": "matching documents",
"search-copy-link-title": "Copy link to search",
"search-hide-matches-text": "Hide additional matches",
"search-more-match-text": "more match in this document",
"search-more-matches-text": "more matches in this document",
"search-clear-button-title": "Clear",
"search-text-placeholder": "",
"search-detached-cancel-button-title": "Cancel",
"search-submit-button-title": "Submit",
"search-label": "Search"
}
}</script>
<script async="" src="https://www.googletagmanager.com/gtag/js?id=G-ZEMGTY4VV3"></script>
<script type="text/javascript">
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-ZEMGTY4VV3', { 'anonymize_ip': true});
</script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/require.js/2.3.6/require.min.js" integrity="sha512-c3Nl8+7g4LMSTdrm621y7kf9v3SDPnhxLNhcjFJbKECVnmZHTdo+IRO05sNLTH/D3vA6u1X32ehoLC7WFVdheg==" crossorigin="anonymous"></script>
<script type="application/javascript">define('jquery', [],function() {return window.jQuery;})</script>
<script src="https://cdnjs.cloudflare.com/polyfill/v3/polyfill.min.js?features=es6"></script>
<script src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml-full.js" type="text/javascript"></script>
<script type="text/javascript">
const typesetMath = (el) => {
if (window.MathJax) {
// MathJax Typeset
window.MathJax.typeset([el]);
} else if (window.katex) {
// KaTeX Render
var mathElements = el.getElementsByClassName("math");
var macros = [];
for (var i = 0; i < mathElements.length; i++) {
var texText = mathElements[i].firstChild;
if (mathElements[i].tagName == "SPAN") {
window.katex.render(texText.data, mathElements[i], {
displayMode: mathElements[i].classList.contains('display'),
throwOnError: false,
macros: macros,
fleqn: false
});
}
}
}
}
window.Quarto = {
typesetMath
};
</script>
<link rel="stylesheet" href="helpers/mystyle.css">
</head>
<body class="nav-sidebar floating">
<div id="quarto-search-results"></div>
<header id="quarto-header" class="headroom fixed-top">
<nav class="quarto-secondary-nav">
<div class="container-fluid d-flex">
<button type="button" class="quarto-btn-toggle btn" data-bs-toggle="collapse" role="button" data-bs-target=".quarto-sidebar-collapse-item" aria-controls="quarto-sidebar" aria-expanded="false" aria-label="Toggle sidebar navigation" onclick="if (window.quartoToggleHeadroom) { window.quartoToggleHeadroom(); }">
<i class="bi bi-layout-text-sidebar-reverse"></i>
</button>
<nav class="quarto-page-breadcrumbs" aria-label="breadcrumb"><ol class="breadcrumb"><li class="breadcrumb-item"><a href="./04-geometry-operations.html"><span class="chapter-number">4</span> <span class="chapter-title">Geometry operations</span></a></li></ol></nav>
<a class="flex-grow-1" role="navigation" data-bs-toggle="collapse" data-bs-target=".quarto-sidebar-collapse-item" aria-controls="quarto-sidebar" aria-expanded="false" aria-label="Toggle sidebar navigation" onclick="if (window.quartoToggleHeadroom) { window.quartoToggleHeadroom(); }">
</a>
<button type="button" class="btn quarto-search-button" aria-label="Search" onclick="window.quartoOpenSearch();">
<i class="bi bi-search"></i>
</button>
</div>
</nav>
</header>
<!-- content -->
<div id="quarto-content" class="quarto-container page-columns page-rows-contents page-layout-article">
<!-- sidebar -->
<nav id="quarto-sidebar" class="sidebar collapse collapse-horizontal quarto-sidebar-collapse-item sidebar-navigation floating overflow-auto">
<div class="pt-lg-2 mt-2 text-left sidebar-header">
<div class="sidebar-title mb-0 py-0">
<a href="./">Geocomputation with Python</a>
<div class="sidebar-tools-main">
<a href="https://github.com/geocompx/geocompy/" title="Source Code" class="quarto-navigation-tool px-1" aria-label="Source Code"><i class="bi bi-github"></i></a>
<div class="dropdown">
<a href="" title="Share" id="quarto-navigation-tool-dropdown-0" class="quarto-navigation-tool dropdown-toggle px-1" data-bs-toggle="dropdown" aria-expanded="false" role="link" aria-label="Share"><i class="bi bi-share"></i></a>
<ul class="dropdown-menu" aria-labelledby="quarto-navigation-tool-dropdown-0">
<li>
<a class="dropdown-item sidebar-tools-main-item" href="https://twitter.com/intent/tweet?url=|url|">
<i class="bi bi-twitter pe-1"></i>
Twitter
</a>
</li>
<li>
<a class="dropdown-item sidebar-tools-main-item" href="https://www.facebook.com/sharer/sharer.php?u=|url|">
<i class="bi bi-facebook pe-1"></i>
Facebook
</a>
</li>
<li>
<a class="dropdown-item sidebar-tools-main-item" href="https://www.linkedin.com/sharing/share-offsite/?url=|url|">
<i class="bi bi-linkedin pe-1"></i>
LinkedIn
</a>
</li>
</ul>
</div>
</div>
</div>
</div>
<div class="mt-2 flex-shrink-0 align-items-center">
<div class="sidebar-search">
<div id="quarto-search" class="" title="Search"></div>
</div>
</div>
<div class="sidebar-menu-container">
<ul class="list-unstyled mt-1">
<li class="sidebar-item">
<div class="sidebar-item-container">
<a href="./index.html" class="sidebar-item-text sidebar-link">
<span class="menu-text">Welcome</span></a>
</div>
</li>
<li class="sidebar-item">
<div class="sidebar-item-container">
<a href="./preface.html" class="sidebar-item-text sidebar-link">
<span class="menu-text">Preface</span></a>
</div>
</li>
<li class="sidebar-item">
<div class="sidebar-item-container">
<a href="./01-spatial-data.html" class="sidebar-item-text sidebar-link">
<span class="menu-text"><span class="chapter-number">1</span> <span class="chapter-title">Geographic data in Python</span></span></a>
</div>
</li>
<li class="sidebar-item">
<div class="sidebar-item-container">
<a href="./02-attribute-operations.html" class="sidebar-item-text sidebar-link">
<span class="menu-text"><span class="chapter-number">2</span> <span class="chapter-title">Attribute data operations</span></span></a>
</div>
</li>
<li class="sidebar-item">
<div class="sidebar-item-container">
<a href="./03-spatial-operations.html" class="sidebar-item-text sidebar-link">
<span class="menu-text"><span class="chapter-number">3</span> <span class="chapter-title">Spatial data operations</span></span></a>
</div>
</li>
<li class="sidebar-item">
<div class="sidebar-item-container">
<a href="./04-geometry-operations.html" class="sidebar-item-text sidebar-link active">
<span class="menu-text"><span class="chapter-number">4</span> <span class="chapter-title">Geometry operations</span></span></a>
</div>
</li>
<li class="sidebar-item">
<div class="sidebar-item-container">
<a href="./05-raster-vector.html" class="sidebar-item-text sidebar-link">
<span class="menu-text"><span class="chapter-number">5</span> <span class="chapter-title">Raster-vector interactions</span></span></a>
</div>
</li>
<li class="sidebar-item">
<div class="sidebar-item-container">
<a href="./06-reproj.html" class="sidebar-item-text sidebar-link">
<span class="menu-text"><span class="chapter-number">6</span> <span class="chapter-title">Reprojecting geographic data</span></span></a>
</div>
</li>
<li class="sidebar-item">
<div class="sidebar-item-container">
<a href="./07-read-write.html" class="sidebar-item-text sidebar-link">
<span class="menu-text"><span class="chapter-number">7</span> <span class="chapter-title">Geographic data I/O</span></span></a>
</div>
</li>
<li class="sidebar-item">
<div class="sidebar-item-container">
<a href="./08-mapping.html" class="sidebar-item-text sidebar-link">
<span class="menu-text"><span class="chapter-number">8</span> <span class="chapter-title">Making maps with Python</span></span></a>
</div>
</li>
<li class="sidebar-item">
<div class="sidebar-item-container">
<a href="./references.html" class="sidebar-item-text sidebar-link">
<span class="menu-text">References</span></a>
</div>
</li>
</ul>
</div>
</nav>
<div id="quarto-sidebar-glass" class="quarto-sidebar-collapse-item" data-bs-toggle="collapse" data-bs-target=".quarto-sidebar-collapse-item"></div>
<!-- margin-sidebar -->
<div id="quarto-margin-sidebar" class="sidebar margin-sidebar">
<nav id="TOC" role="doc-toc" class="toc-active">
<h2 class="anchored">First Edition</h2>
<ul>
<li><a href="https://geocompx.org/" class="nav-link active" data-scroll-target="https\://geocompx.org/">Visit the geocompx website 🌐</a></li>
<li><a href="https://github.com/geocompx/geocompy/issues" class="nav-link" data-scroll-target="https\://github.com/geocompx/geocompy/issues">Open an issue ❔</a></li>
<li><a href="https://discord.gg/PMztXYgNxp" class="nav-link" data-scroll-target="https\://discord.gg/PMztXYgNxp">Chat on Discord 📣</a></li>
<li><a href="https://donate.stripe.com/4gweWl94Q9E35AQ6oo" class="nav-link" data-scroll-target="https\://donate.stripe.com/4gweWl94Q9E35AQ6oo">Support this project 💸</a></li>
</ul>
<hr>
<h2 id="toc-title">On this page</h2>
<ul>
<li><a href="#prerequisites" id="toc-prerequisites" class="nav-link" data-scroll-target="#prerequisites">Prerequisites</a></li>
<li><a href="#introduction" id="toc-introduction" class="nav-link" data-scroll-target="#introduction"><span class="header-section-number">4.1</span> Introduction</a></li>
<li><a href="#sec-geo-vec" id="toc-sec-geo-vec" class="nav-link" data-scroll-target="#sec-geo-vec"><span class="header-section-number">4.2</span> Geometric operations on vector data</a>
<ul>
<li><a href="#sec-simplification" id="toc-sec-simplification" class="nav-link" data-scroll-target="#sec-simplification"><span class="header-section-number">4.2.1</span> Simplification</a></li>
<li><a href="#sec-centroids" id="toc-sec-centroids" class="nav-link" data-scroll-target="#sec-centroids"><span class="header-section-number">4.2.2</span> Centroids</a></li>
<li><a href="#sec-buffers" id="toc-sec-buffers" class="nav-link" data-scroll-target="#sec-buffers"><span class="header-section-number">4.2.3</span> Buffers</a></li>
<li><a href="#sec-affine-transformations" id="toc-sec-affine-transformations" class="nav-link" data-scroll-target="#sec-affine-transformations"><span class="header-section-number">4.2.4</span> Affine transformations</a></li>
<li><a href="#sec-clipping" id="toc-sec-clipping" class="nav-link" data-scroll-target="#sec-clipping"><span class="header-section-number">4.2.5</span> Pairwise geometry-generating operations</a></li>
<li><a href="#sec-subsetting-vs-clipping" id="toc-sec-subsetting-vs-clipping" class="nav-link" data-scroll-target="#sec-subsetting-vs-clipping"><span class="header-section-number">4.2.6</span> Subsetting vs. clipping</a></li>
<li><a href="#sec-geometry-unions" id="toc-sec-geometry-unions" class="nav-link" data-scroll-target="#sec-geometry-unions"><span class="header-section-number">4.2.7</span> Geometry unions</a></li>
<li><a href="#sec-type-transformations" id="toc-sec-type-transformations" class="nav-link" data-scroll-target="#sec-type-transformations"><span class="header-section-number">4.2.8</span> Type transformations</a></li>
</ul></li>
<li><a href="#sec-geo-ras" id="toc-sec-geo-ras" class="nav-link" data-scroll-target="#sec-geo-ras"><span class="header-section-number">4.3</span> Geometric operations on raster data</a>
<ul>
<li><a href="#sec-extent-and-origin" id="toc-sec-extent-and-origin" class="nav-link" data-scroll-target="#sec-extent-and-origin"><span class="header-section-number">4.3.1</span> Extent and origin</a></li>
<li><a href="#sec-raster-agg-disagg" id="toc-sec-raster-agg-disagg" class="nav-link" data-scroll-target="#sec-raster-agg-disagg"><span class="header-section-number">4.3.2</span> Aggregation and disaggregation</a></li>
<li><a href="#sec-raster-resampling" id="toc-sec-raster-resampling" class="nav-link" data-scroll-target="#sec-raster-resampling"><span class="header-section-number">4.3.3</span> Resampling</a></li>
</ul></li>
</ul>
<div class="toc-actions"><ul><li><a href="https://github.com/geocompx/geocompy/edit/main/04-geometry-operations.qmd" class="toc-action"><i class="bi bi-github"></i>Edit this page</a></li></ul></div></nav>
</div>
<!-- main -->
<main class="content" id="quarto-document-content">
<header id="title-block-header" class="quarto-title-block default">
<div class="quarto-title">
<h1 class="title"><span id="sec-geometric-operations" class="quarto-section-identifier"><span class="chapter-number">4</span> <span class="chapter-title">Geometry operations</span></span></h1>
</div>
<!--
-->
<div class="quarto-title-meta">
</div>
</header>
<section id="prerequisites" class="level2 unnumbered">
<h2 class="unnumbered anchored" data-anchor-id="prerequisites">Prerequisites</h2>
<p>This chapter requires importing the following packages:</p>
<div id="8cc4e3e3" class="cell" data-execution_count="3">
<div class="sourceCode cell-code" id="cb1"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> sys</span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> numpy <span class="im">as</span> np</span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> matplotlib.pyplot <span class="im">as</span> plt</span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> pandas <span class="im">as</span> pd</span>
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> shapely</span>
<span id="cb1-6"><a href="#cb1-6" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> geopandas <span class="im">as</span> gpd</span>
<span id="cb1-7"><a href="#cb1-7" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> topojson <span class="im">as</span> tp</span>
<span id="cb1-8"><a href="#cb1-8" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> rasterio</span>
<span id="cb1-9"><a href="#cb1-9" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> rasterio.plot</span>
<span id="cb1-10"><a href="#cb1-10" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> rasterio.warp</span>
<span id="cb1-11"><a href="#cb1-11" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> rasterio.mask</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
</div>
<p>It also relies on the following data files:</p>
<div id="3c3cf290" class="cell" data-execution_count="4">
<div class="sourceCode cell-code" id="cb2"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a>seine <span class="op">=</span> gpd.read_file(<span class="st">'data/seine.gpkg'</span>)</span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a>us_states <span class="op">=</span> gpd.read_file(<span class="st">'data/us_states.gpkg'</span>)</span>
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a>nz <span class="op">=</span> gpd.read_file(<span class="st">'data/nz.gpkg'</span>)</span>
<span id="cb2-4"><a href="#cb2-4" aria-hidden="true" tabindex="-1"></a>src <span class="op">=</span> rasterio.<span class="bu">open</span>(<span class="st">'data/dem.tif'</span>)</span>
<span id="cb2-5"><a href="#cb2-5" aria-hidden="true" tabindex="-1"></a>src_elev <span class="op">=</span> rasterio.<span class="bu">open</span>(<span class="st">'output/elev.tif'</span>)</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
</div>
</section>
<section id="introduction" class="level2" data-number="4.1">
<h2 data-number="4.1" class="anchored" data-anchor-id="introduction"><span class="header-section-number">4.1</span> Introduction</h2>
<p>So far the book has explained the structure of geographic datasets (<a href="01-spatial-data.html" class="quarto-xref"><span>Chapter 1</span></a>), and how to manipulate them based on their non-geographic attributes (<a href="02-attribute-operations.html" class="quarto-xref"><span>Chapter 2</span></a>) and spatial relations (<a href="03-spatial-operations.html" class="quarto-xref"><span>Chapter 3</span></a>). This chapter focuses on manipulating the geographic elements of geographic objects, for example by simplifying and converting vector geometries, and by cropping raster datasets. After reading it you should understand and have control over the geometry column in vector layers and the extent and geographic location of pixels represented in rasters in relation to other geographic objects.</p>
<p><a href="#sec-geo-vec" class="quarto-xref"><span>Section 4.2</span></a> covers transforming vector geometries with ‘unary’ and ‘binary’ operations. Unary operations work on a single geometry in isolation, including simplification (of lines and polygons), the creation of buffers and centroids, and shifting/scaling/rotating single geometries using ‘affine transformations’ (<a href="#sec-simplification" class="quarto-xref"><span>Section 4.2.1</span></a> to <a href="#sec-affine-transformations" class="quarto-xref"><span>Section 4.2.4</span></a>). Binary transformations modify one geometry based on the shape of another, including clipping and geometry unions, covered in <a href="#sec-clipping" class="quarto-xref"><span>Section 4.2.5</span></a> and <a href="#sec-geometry-unions" class="quarto-xref"><span>Section 4.2.7</span></a>, respectively. Type transformations (from a polygon to a line, for example) are demonstrated in <a href="#sec-type-transformations" class="quarto-xref"><span>Section 4.2.8</span></a>.</p>
<p><a href="#sec-geo-ras" class="quarto-xref"><span>Section 4.3</span></a> covers geometric transformations on raster objects. This involves changing the size and number of the underlying pixels, and assigning them new values. It teaches how to change the extent and the origin of a raster manually (<a href="#sec-extent-and-origin" class="quarto-xref"><span>Section 4.3.1</span></a>), how to change the resolution in fixed steps through aggregation and disaggregation (<a href="#sec-raster-agg-disagg" class="quarto-xref"><span>Section 4.3.2</span></a>), and finally how to resample a raster into any existing template, which is the most general and often most practical approach (<a href="#sec-raster-resampling" class="quarto-xref"><span>Section 4.3.3</span></a>). These operations are especially useful if one would like to align raster datasets from diverse sources. Aligned raster objects share a one-to-one correspondence between pixels, allowing them to be processed using map algebra operations (<a href="03-spatial-operations.html#sec-raster-local-operations" class="quarto-xref"><span>Section 3.3.3</span></a>).</p>
<p>In the next chapter (<a href="05-raster-vector.html" class="quarto-xref"><span>Chapter 5</span></a>), we deal with the special case of geometry operations that involve both a raster and a vector layer together. It shows how raster values can be ‘masked’ and ‘extracted’ by vector geometries. Importantly it shows how to ‘polygonize’ rasters and ‘rasterize’ vector datasets, making the two data models more interchangeable.</p>
</section>
<section id="sec-geo-vec" class="level2" data-number="4.2">
<h2 data-number="4.2" class="anchored" data-anchor-id="sec-geo-vec"><span class="header-section-number">4.2</span> Geometric operations on vector data</h2>
<p>This section is about operations that in some way change the geometry of vector layers. It is more advanced than the spatial data operations presented in the previous chapter (in <a href="03-spatial-operations.html#sec-spatial-vec" class="quarto-xref"><span>Section 3.2</span></a>), because here we drill down into the geometry: the functions discussed in this section work on the geometric part (the geometry column, which is a <code>GeoSeries</code> object), either as standalone object or as part of a <code>GeoDataFrame</code>.</p>
<section id="sec-simplification" class="level3" data-number="4.2.1">
<h3 data-number="4.2.1" class="anchored" data-anchor-id="sec-simplification"><span class="header-section-number">4.2.1</span> Simplification</h3>
<p>Simplification is a process for generalization of vector objects (lines and polygons) usually for use in smaller-scale maps. Another reason for simplifying objects is to reduce the amount of memory, disk space, and network bandwidth they consume: it may be wise to simplify complex geometries before publishing them as interactive maps. The <strong>geopandas</strong> package provides the <code>.simplify</code> method, which uses the GEOS implementation of the Douglas-Peucker algorithm to reduce the vertex count. <code>.simplify</code> uses <code>tolerance</code> to control the level of generalization in map units <span class="citation" data-cites="douglas_algorithms_1973">(<a href="references.html#ref-douglas_algorithms_1973" role="doc-biblioref">Douglas and Peucker 1973</a>)</span>.</p>
<p>For example, a simplified geometry of a <code>'LineString'</code> geometry, representing the river Seine and tributaries, using tolerance of <code>2000</code> meters, can be created using the <code>seine.simplify(2000)</code> command (<a href="#fig-simplify-lines" class="quarto-xref">Figure <span>4.1</span></a>).</p>
<div class="sourceCode cell-code" id="cb3"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a>seine_simp <span class="op">=</span> seine.simplify(<span class="dv">2000</span>)</span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a>seine.plot()<span class="op">;</span></span>
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a>seine_simp.plot()<span class="op">;</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<div id="fig-simplify-lines" class="quarto-layout-panel">
<figure class="quarto-float quarto-float-fig figure">
<div aria-describedby="fig-simplify-lines-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
<div class="quarto-layout-row">
<div class="cell-output cell-output-display quarto-layout-cell-subref quarto-layout-cell" data-ref-parent="fig-simplify-lines" style="flex-basis: 50.0%;justify-content: flex-start;">
<div id="fig-simplify-lines-1" class="quarto-float quarto-figure quarto-figure-center anchored">
<figure class="quarto-float quarto-subfloat-fig figure">
<div aria-describedby="fig-simplify-lines-1-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
<img src="04-geometry-operations_files/figure-html/fig-simplify-lines-output-1.png" data-ref-parent="fig-simplify-lines" width="430" height="342" class="figure-img">
</div>
<figcaption class="quarto-float-caption-bottom quarto-subfloat-caption quarto-subfloat-fig" id="fig-simplify-lines-1-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
(a) Original
</figcaption>
</figure>
</div>
</div>
<div class="cell-output cell-output-display quarto-layout-cell-subref quarto-layout-cell" data-ref-parent="fig-simplify-lines" style="flex-basis: 50.0%;justify-content: flex-start;">
<div id="fig-simplify-lines-2" class="quarto-float quarto-figure quarto-figure-center anchored">
<figure class="quarto-float quarto-subfloat-fig figure">
<div aria-describedby="fig-simplify-lines-2-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
<img src="04-geometry-operations_files/figure-html/fig-simplify-lines-output-2.png" data-ref-parent="fig-simplify-lines" width="430" height="341" class="figure-img">
</div>
<figcaption class="quarto-float-caption-bottom quarto-subfloat-caption quarto-subfloat-fig" id="fig-simplify-lines-2-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
(b) Simplified (tolerance = 2000 <span class="math inline">\(m\)</span>)
</figcaption>
</figure>
</div>
</div>
</div>
</div>
<figcaption class="quarto-float-caption-bottom quarto-float-caption quarto-float-fig" id="fig-simplify-lines-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
Figure 4.1: Simplification of the <code>seine</code> line layer
</figcaption>
</figure>
</div>
<p>The resulting <code>seine_simp</code> object is a copy of the original <code>seine</code> but with fewer vertices. This is apparent, with the result being visually simpler (<a href="#fig-simplify-lines" class="quarto-xref">Figure <span>4.1</span></a>, right) and consuming about twice less memory than the original object, as shown in the comparison below.</p>
<div id="26de90dc" class="cell" data-execution_count="6">
<div class="sourceCode cell-code" id="cb4"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="bu">print</span>(<span class="ss">f'Original: </span><span class="sc">{</span>sys<span class="sc">.</span>getsizeof(seine)<span class="sc">}</span><span class="ss"> bytes'</span>)</span>
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a><span class="bu">print</span>(<span class="ss">f'Simplified: </span><span class="sc">{</span>sys<span class="sc">.</span>getsizeof(seine_simp)<span class="sc">}</span><span class="ss"> bytes'</span>)</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<div class="cell-output cell-output-stdout">
<pre><code>Original: 374 bytes
Simplified: 188 bytes</code></pre>
</div>
</div>
<p>Simplification is also applicable for polygons. This is illustrated using <code>us_states</code>, representing the contiguous United States. As we show in <a href="06-reproj.html" class="quarto-xref"><span>Chapter 6</span></a>, for many calculations <strong>geopandas</strong> (through <strong>shapely</strong>, and, ultimately, GEOS) assumes that the data is in a projected CRS and this could lead to unexpected results when applying distance-related operators. Therefore, the first step is to project the data into some adequate projected CRS, such as US National Atlas Equal Area (EPSG:<code>9311</code>) (on the left in Figure <a href="#fig-simplify-polygons" class="quarto-xref">Figure <span>4.2</span></a>), using <code>.to_crs</code> (<a href="06-reproj.html#sec-reprojecting-vector-geometries" class="quarto-xref"><span>Section 6.7</span></a>).</p>
<div id="3f90087c" class="cell" data-execution_count="7">
<div class="sourceCode cell-code" id="cb6"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true" tabindex="-1"></a>us_states9311 <span class="op">=</span> us_states.to_crs(<span class="dv">9311</span>)</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
</div>
<p>The <code>.simplify</code> method from <strong>geopandas</strong> works the same way with a <code>'Polygon'</code>/<code>'MultiPolygon'</code> layer such as <code>us_states9311</code>:</p>
<div id="813468b4" class="cell" data-execution_count="8">
<div class="sourceCode cell-code" id="cb7"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true" tabindex="-1"></a>us_states_simp1 <span class="op">=</span> us_states9311.simplify(<span class="dv">100000</span>)</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
</div>
<p>A limitation with <code>.simplify</code>, however, is that it simplifies objects on a per-geometry basis. This means the topology is lost, resulting in overlapping and ‘holey’ areal units as illustrated in <a href="#fig-simplify-polygons" class="quarto-xref">Figure <span>4.2</span></a> (b). The <code>.toposimplify</code> method from package <strong>topojson</strong> provides an alternative that overcomes this issue. The main advanatage of <code>.toposimplify</code> is that it is topologically ‘aware’: it simplifies the combined borders of the polygons (rather than each polygon on its own), thus ensuring that the overlap is maintained. The following code chunk uses <code>.toposimplify</code> to simplify <code>us_states9311</code>. Note that, when using the <strong>topojson</strong> package, we first need to calculate a topology object, using function <code>tp.Topology</code>, and then apply the simplification function, such as <code>.toposimplify</code>, to obtain a simplified layer. We are also using the <code>.to_gdf</code> method to return a <code>GeoDataFrame</code>.</p>
<div id="c41bcf06" class="cell" data-execution_count="9">
<div class="sourceCode cell-code" id="cb8"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb8-1"><a href="#cb8-1" aria-hidden="true" tabindex="-1"></a>topo <span class="op">=</span> tp.Topology(us_states9311, prequantize<span class="op">=</span><span class="va">False</span>)</span>
<span id="cb8-2"><a href="#cb8-2" aria-hidden="true" tabindex="-1"></a>us_states_simp2 <span class="op">=</span> topo.toposimplify(<span class="dv">100000</span>).to_gdf()</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
</div>
<p><a href="#fig-simplify-polygons" class="quarto-xref">Figure <span>4.2</span></a> compares the original input polygons and two simplification methods applied to <code>us_states9311</code>.</p>
<div class="sourceCode cell-code" id="cb9"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb9-1"><a href="#cb9-1" aria-hidden="true" tabindex="-1"></a>us_states9311.plot(color<span class="op">=</span><span class="st">'lightgrey'</span>, edgecolor<span class="op">=</span><span class="st">'black'</span>)<span class="op">;</span></span>
<span id="cb9-2"><a href="#cb9-2" aria-hidden="true" tabindex="-1"></a>us_states_simp1.plot(color<span class="op">=</span><span class="st">'lightgrey'</span>, edgecolor<span class="op">=</span><span class="st">'black'</span>)<span class="op">;</span></span>
<span id="cb9-3"><a href="#cb9-3" aria-hidden="true" tabindex="-1"></a>us_states_simp2.plot(color<span class="op">=</span><span class="st">'lightgrey'</span>, edgecolor<span class="op">=</span><span class="st">'black'</span>)<span class="op">;</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<div id="fig-simplify-polygons" class="quarto-layout-panel">
<figure class="quarto-float quarto-float-fig figure">
<div aria-describedby="fig-simplify-polygons-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
<div class="quarto-layout-row">
<div class="cell-output cell-output-display quarto-layout-cell-subref quarto-layout-cell" data-ref-parent="fig-simplify-polygons" style="flex-basis: 33.3%;justify-content: flex-start;">
<div id="fig-simplify-polygons-1" class="quarto-float quarto-figure quarto-figure-center anchored">
<figure class="quarto-float quarto-subfloat-fig figure">
<div aria-describedby="fig-simplify-polygons-1-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
<img src="04-geometry-operations_files/figure-html/fig-simplify-polygons-output-1.png" data-ref-parent="fig-simplify-polygons" width="433" height="306" class="figure-img">
</div>
<figcaption class="quarto-float-caption-bottom quarto-subfloat-caption quarto-subfloat-fig" id="fig-simplify-polygons-1-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
(a) Original
</figcaption>
</figure>
</div>
</div>
<div class="cell-output cell-output-display quarto-layout-cell-subref quarto-layout-cell" data-ref-parent="fig-simplify-polygons" style="flex-basis: 33.3%;justify-content: flex-start;">
<div id="fig-simplify-polygons-2" class="quarto-float quarto-figure quarto-figure-center anchored">
<figure class="quarto-float quarto-subfloat-fig figure">
<div aria-describedby="fig-simplify-polygons-2-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
<img src="04-geometry-operations_files/figure-html/fig-simplify-polygons-output-2.png" data-ref-parent="fig-simplify-polygons" width="433" height="309" class="figure-img">
</div>
<figcaption class="quarto-float-caption-bottom quarto-subfloat-caption quarto-subfloat-fig" id="fig-simplify-polygons-2-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
(b) Simplified using <strong>geopandas</strong>
</figcaption>
</figure>
</div>
</div>
<div class="cell-output cell-output-display quarto-layout-cell-subref quarto-layout-cell" data-ref-parent="fig-simplify-polygons" style="flex-basis: 33.3%;justify-content: flex-start;">
<div id="fig-simplify-polygons-3" class="quarto-float quarto-figure quarto-figure-center anchored">
<figure class="quarto-float quarto-subfloat-fig figure">
<div aria-describedby="fig-simplify-polygons-3-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
<img src="04-geometry-operations_files/figure-html/fig-simplify-polygons-output-3.png" data-ref-parent="fig-simplify-polygons" width="433" height="308" class="figure-img">
</div>
<figcaption class="quarto-float-caption-bottom quarto-subfloat-caption quarto-subfloat-fig" id="fig-simplify-polygons-3-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
(c) Simplified using <strong>topojson</strong>
</figcaption>
</figure>
</div>
</div>
</div>
</div>
<figcaption class="quarto-float-caption-bottom quarto-float-caption quarto-float-fig" id="fig-simplify-polygons-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
Figure 4.2: Polygon simplification in action, comparing the original geometry of the contiguous United States with simplified versions, generated with functions from the <strong>geopandas</strong> (middle), and <strong>topojson</strong> (right), packages.
</figcaption>
</figure>
</div>
</section>
<section id="sec-centroids" class="level3" data-number="4.2.2">
<h3 data-number="4.2.2" class="anchored" data-anchor-id="sec-centroids"><span class="header-section-number">4.2.2</span> Centroids</h3>
<p>Centroid operations identify the center of geographic objects. Like statistical measures of central tendency (including mean and median definitions of ‘average’), there are many ways to define the geographic center of an object. All of them create single-point representations of more complex vector objects.</p>
<p>The most commonly used centroid operation is the geographic centroid. This type of centroid operation (often referred to as ‘the centroid’) represents the center of mass in a spatial object (think of balancing a plate on your finger). Geographic centroids have many uses, for example to create a simple point representation of complex geometries, to estimate distances between polygons, or to specify the location where polygon text labels are placed. Centroids of the geometries in a <code>GeoSeries</code> or a <code>GeoDataFrame</code> are accessible through the <code>.centroid</code> property, as demonstrated in the code below, which generates the geographic centroids of regions in New Zealand and tributaries to the River Seine (black points in <a href="#fig-centroid-pnt-on-surface" class="quarto-xref">Figure <span>4.3</span></a>).</p>
<div id="68475cb2" class="cell" data-execution_count="11">
<div class="sourceCode cell-code" id="cb10"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb10-1"><a href="#cb10-1" aria-hidden="true" tabindex="-1"></a>nz_centroid <span class="op">=</span> nz.centroid</span>
<span id="cb10-2"><a href="#cb10-2" aria-hidden="true" tabindex="-1"></a>seine_centroid <span class="op">=</span> seine.centroid</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
</div>
<p>Sometimes the geographic centroid falls outside the boundaries of their parent objects (think of vector data in shape of a doughnut). In such cases ‘point on surface’ operations, created with the <code>.representative_point</code> method, can be used to guarantee the point will be in the parent object (e.g., for labeling irregular multipolygon objects such as island states), as illustrated by the red points in <a href="#fig-centroid-pnt-on-surface" class="quarto-xref">Figure <span>4.3</span></a>. Notice that these red points always lie on their parent objects.</p>
<div id="8f299062" class="cell" data-execution_count="12">
<div class="sourceCode cell-code" id="cb11"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb11-1"><a href="#cb11-1" aria-hidden="true" tabindex="-1"></a>nz_pos <span class="op">=</span> nz.representative_point()</span>
<span id="cb11-2"><a href="#cb11-2" aria-hidden="true" tabindex="-1"></a>seine_pos <span class="op">=</span> seine.representative_point()</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
</div>
<p>The centroids and points on surface are illustrated in <a href="#fig-centroid-pnt-on-surface" class="quarto-xref">Figure <span>4.3</span></a>.</p>
<div class="sourceCode cell-code" id="cb12"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb12-1"><a href="#cb12-1" aria-hidden="true" tabindex="-1"></a><span class="co"># New Zealand</span></span>
<span id="cb12-2"><a href="#cb12-2" aria-hidden="true" tabindex="-1"></a>base <span class="op">=</span> nz.plot(color<span class="op">=</span><span class="st">'white'</span>, edgecolor<span class="op">=</span><span class="st">'lightgrey'</span>)</span>
<span id="cb12-3"><a href="#cb12-3" aria-hidden="true" tabindex="-1"></a>nz_centroid.plot(ax<span class="op">=</span>base, color<span class="op">=</span><span class="st">'None'</span>, edgecolor<span class="op">=</span><span class="st">'black'</span>)</span>
<span id="cb12-4"><a href="#cb12-4" aria-hidden="true" tabindex="-1"></a>nz_pos.plot(ax<span class="op">=</span>base, color<span class="op">=</span><span class="st">'None'</span>, edgecolor<span class="op">=</span><span class="st">'red'</span>)<span class="op">;</span></span>
<span id="cb12-5"><a href="#cb12-5" aria-hidden="true" tabindex="-1"></a><span class="co"># Seine</span></span>
<span id="cb12-6"><a href="#cb12-6" aria-hidden="true" tabindex="-1"></a>base <span class="op">=</span> seine.plot(color<span class="op">=</span><span class="st">'grey'</span>)</span>
<span id="cb12-7"><a href="#cb12-7" aria-hidden="true" tabindex="-1"></a>seine_pos.plot(ax<span class="op">=</span>base, color<span class="op">=</span><span class="st">'None'</span>, edgecolor<span class="op">=</span><span class="st">'red'</span>)</span>
<span id="cb12-8"><a href="#cb12-8" aria-hidden="true" tabindex="-1"></a>seine_centroid.plot(ax<span class="op">=</span>base, color<span class="op">=</span><span class="st">'None'</span>, edgecolor<span class="op">=</span><span class="st">'black'</span>)<span class="op">;</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<div id="fig-centroid-pnt-on-surface" class="quarto-layout-panel">
<figure class="quarto-float quarto-float-fig figure">
<div aria-describedby="fig-centroid-pnt-on-surface-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
<div class="quarto-layout-row">
<div class="cell-output cell-output-display quarto-layout-cell-subref quarto-layout-cell" data-ref-parent="fig-centroid-pnt-on-surface" style="flex-basis: 50.0%;justify-content: flex-start;">
<div id="fig-centroid-pnt-on-surface-1" class="quarto-float quarto-figure quarto-figure-center anchored">
<figure class="quarto-float quarto-subfloat-fig figure">
<div aria-describedby="fig-centroid-pnt-on-surface-1-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
<img src="04-geometry-operations_files/figure-html/fig-centroid-pnt-on-surface-output-1.png" data-ref-parent="fig-centroid-pnt-on-surface" width="306" height="442" class="figure-img">
</div>
<figcaption class="quarto-float-caption-bottom quarto-subfloat-caption quarto-subfloat-fig" id="fig-centroid-pnt-on-surface-1-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
(a) New Zealand
</figcaption>
</figure>
</div>
</div>
<div class="cell-output cell-output-display quarto-layout-cell-subref quarto-layout-cell" data-ref-parent="fig-centroid-pnt-on-surface" style="flex-basis: 50.0%;justify-content: flex-start;">
<div id="fig-centroid-pnt-on-surface-2" class="quarto-float quarto-figure quarto-figure-center anchored">
<figure class="quarto-float quarto-subfloat-fig figure">
<div aria-describedby="fig-centroid-pnt-on-surface-2-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
<img src="04-geometry-operations_files/figure-html/fig-centroid-pnt-on-surface-output-2.png" data-ref-parent="fig-centroid-pnt-on-surface" width="430" height="342" class="figure-img">
</div>
<figcaption class="quarto-float-caption-bottom quarto-subfloat-caption quarto-subfloat-fig" id="fig-centroid-pnt-on-surface-2-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
(b) Seine
</figcaption>
</figure>
</div>
</div>
</div>
</div>
<figcaption class="quarto-float-caption-bottom quarto-float-caption quarto-float-fig" id="fig-centroid-pnt-on-surface-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
Figure 4.3: Centroids (black) and points on surface (red) of New Zealand and Seine datasets.
</figcaption>
</figure>
</div>
</section>
<section id="sec-buffers" class="level3" data-number="4.2.3">
<h3 data-number="4.2.3" class="anchored" data-anchor-id="sec-buffers"><span class="header-section-number">4.2.3</span> Buffers</h3>
<p>Buffers are polygons representing the area within a given distance of a geometric feature: regardless of whether the input is a point, line or polygon, the output is a polygon (when using positive buffer distance). Unlike simplification, which is often used for visualization and reducing file size, buffering tends to be used for geographic data analysis. How many points are within a given distance of this line? Which demographic groups are within travel distance of this new shop? These kinds of questions can be answered and visualized by creating buffers around the geographic entities of interest.</p>
<p><a href="#fig-buffers" class="quarto-xref">Figure <span>4.4</span></a> illustrates buffers of two different sizes (5 and 50 <span class="math inline">\(km\)</span>) surrounding the river Seine and tributaries. These buffers were created with commands below, using the <code>.buffer</code> method, applied to a <code>GeoSeries</code> or <code>GeoDataFrame</code>. The <code>.buffer</code> method requires one important argument: the buffer distance, provided in the units of the CRS, in this case, meters.</p>
<div id="703413c0" class="cell" data-execution_count="14">
<div class="sourceCode cell-code" id="cb13"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb13-1"><a href="#cb13-1" aria-hidden="true" tabindex="-1"></a>seine_buff_5km <span class="op">=</span> seine.<span class="bu">buffer</span>(<span class="dv">5000</span>)</span>
<span id="cb13-2"><a href="#cb13-2" aria-hidden="true" tabindex="-1"></a>seine_buff_50km <span class="op">=</span> seine.<span class="bu">buffer</span>(<span class="dv">50000</span>)</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
</div>
<p>The results are shown in <a href="#fig-buffers" class="quarto-xref">Figure <span>4.4</span></a>.</p>
<div class="sourceCode cell-code" id="cb14"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb14-1"><a href="#cb14-1" aria-hidden="true" tabindex="-1"></a>seine_buff_5km.plot(color<span class="op">=</span><span class="st">'none'</span>, edgecolor<span class="op">=</span>[<span class="st">'c'</span>, <span class="st">'m'</span>, <span class="st">'y'</span>])<span class="op">;</span></span>
<span id="cb14-2"><a href="#cb14-2" aria-hidden="true" tabindex="-1"></a>seine_buff_50km.plot(color<span class="op">=</span><span class="st">'none'</span>, edgecolor<span class="op">=</span>[<span class="st">'c'</span>, <span class="st">'m'</span>, <span class="st">'y'</span>])<span class="op">;</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<div id="fig-buffers" class="quarto-layout-panel">
<figure class="quarto-float quarto-float-fig figure">
<div aria-describedby="fig-buffers-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
<div class="quarto-layout-row">
<div class="cell-output cell-output-display quarto-layout-cell-subref quarto-layout-cell" data-ref-parent="fig-buffers" style="flex-basis: 50.0%;justify-content: flex-start;">
<div id="fig-buffers-1" class="quarto-float quarto-figure quarto-figure-center anchored">
<figure class="quarto-float quarto-subfloat-fig figure">
<div aria-describedby="fig-buffers-1-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
<img src="04-geometry-operations_files/figure-html/fig-buffers-output-1.png" data-ref-parent="fig-buffers" width="452" height="344" class="figure-img">
</div>
<figcaption class="quarto-float-caption-bottom quarto-subfloat-caption quarto-subfloat-fig" id="fig-buffers-1-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
(a) 5 <span class="math inline">\(km\)</span> buffer
</figcaption>
</figure>
</div>
</div>
<div class="cell-output cell-output-display quarto-layout-cell-subref quarto-layout-cell" data-ref-parent="fig-buffers" style="flex-basis: 50.0%;justify-content: flex-start;">
<div id="fig-buffers-2" class="quarto-float quarto-figure quarto-figure-center anchored">
<figure class="quarto-float quarto-subfloat-fig figure">
<div aria-describedby="fig-buffers-2-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
<img src="04-geometry-operations_files/figure-html/fig-buffers-output-2.png" data-ref-parent="fig-buffers" width="430" height="360" class="figure-img">
</div>
<figcaption class="quarto-float-caption-bottom quarto-subfloat-caption quarto-subfloat-fig" id="fig-buffers-2-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
(b) 50 <span class="math inline">\(km\)</span> buffer
</figcaption>
</figure>
</div>
</div>
</div>
</div>
<figcaption class="quarto-float-caption-bottom quarto-float-caption quarto-float-fig" id="fig-buffers-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
Figure 4.4: Buffers around the Seine dataset of 5 <span class="math inline">\(km\)</span> and 50 <span class="math inline">\(km\)</span>. Note the colors, which reflect the fact that one buffer is created per geometry feature.
</figcaption>
</figure>
</div>
<p>Note that both <code>.centroid</code> and <code>.buffer</code> return a <code>GeoSeries</code> object, even when the input is a <code>GeoDataFrame</code>.</p>
<div id="50eee316" class="cell" data-execution_count="16">
<div class="sourceCode cell-code" id="cb15"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb15-1"><a href="#cb15-1" aria-hidden="true" tabindex="-1"></a>seine_buff_5km</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<div class="cell-output cell-output-display" data-execution_count="16">
<pre><code>0 POLYGON ((657550.332 6852587.97...
1 POLYGON ((517151.801 6930724.10...
2 POLYGON ((701519.74 6813075.492...
dtype: geometry</code></pre>
</div>
</div>
<p>In the common scenario when the original attributes of the input features need to be retained, you can replace the existing geometry with the new <code>GeoSeries</code> by creating a copy of the original <code>GeoDataFrame</code> and assigning the new buffer <code>GeoSeries</code> to the <code>geometry</code> column.</p>
<div id="107d1676" class="cell" data-execution_count="17">
<div class="sourceCode cell-code" id="cb17"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb17-1"><a href="#cb17-1" aria-hidden="true" tabindex="-1"></a>seine_buff_5km <span class="op">=</span> seine.copy()</span>
<span id="cb17-2"><a href="#cb17-2" aria-hidden="true" tabindex="-1"></a>seine_buff_5km.geometry <span class="op">=</span> seine.<span class="bu">buffer</span>(<span class="dv">5000</span>)</span>
<span id="cb17-3"><a href="#cb17-3" aria-hidden="true" tabindex="-1"></a>seine_buff_5km</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<div class="cell-output cell-output-display" data-execution_count="17">
<div>
<table class="dataframe caption-top table table-sm table-striped small" data-quarto-postprocess="true" data-border="1">
<thead>
<tr class="header">
<th data-quarto-table-cell-role="th"></th>
<th data-quarto-table-cell-role="th">name</th>
<th data-quarto-table-cell-role="th">geometry</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td data-quarto-table-cell-role="th">0</td>
<td>Marne</td>
<td>POLYGON ((657550.332 6852587.97...</td>
</tr>
<tr class="even">
<td data-quarto-table-cell-role="th">1</td>
<td>Seine</td>
<td>POLYGON ((517151.801 6930724.10...</td>
</tr>
<tr class="odd">
<td data-quarto-table-cell-role="th">2</td>
<td>Yonne</td>
<td>POLYGON ((701519.74 6813075.492...</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<p>An alternative option is to add a secondary geometry column directly to the original <code>GeoDataFrame</code>.</p>
<div id="98dde783" class="cell" data-execution_count="18">
<div class="sourceCode cell-code" id="cb18"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb18-1"><a href="#cb18-1" aria-hidden="true" tabindex="-1"></a>seine[<span class="st">'geometry_5km'</span>] <span class="op">=</span> seine.<span class="bu">buffer</span>(<span class="dv">5000</span>)</span>
<span id="cb18-2"><a href="#cb18-2" aria-hidden="true" tabindex="-1"></a>seine</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<div class="cell-output cell-output-display" data-execution_count="18">
<div>
<table class="dataframe caption-top table table-sm table-striped small" data-quarto-postprocess="true" data-border="1">
<thead>
<tr class="header">
<th data-quarto-table-cell-role="th"></th>
<th data-quarto-table-cell-role="th">name</th>
<th data-quarto-table-cell-role="th">geometry</th>
<th data-quarto-table-cell-role="th">geometry_5km</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td data-quarto-table-cell-role="th">0</td>
<td>Marne</td>
<td>MULTILINESTRING ((879955.277 67...</td>
<td>POLYGON ((657550.332 6852587.97...</td>
</tr>
<tr class="even">
<td data-quarto-table-cell-role="th">1</td>
<td>Seine</td>
<td>MULTILINESTRING ((828893.615 67...</td>
<td>POLYGON ((517151.801 6930724.10...</td>
</tr>
<tr class="odd">
<td data-quarto-table-cell-role="th">2</td>
<td>Yonne</td>
<td>MULTILINESTRING ((773482.137 66...</td>
<td>POLYGON ((701519.74 6813075.492...</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<p>You can then switch to either geometry column (i.e., make it ‘active’) using <code>.set_geometry</code>, as in:</p>
<div id="3fb6435d" class="cell" data-execution_count="19">
<div class="sourceCode cell-code" id="cb19"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb19-1"><a href="#cb19-1" aria-hidden="true" tabindex="-1"></a>seine <span class="op">=</span> seine.set_geometry(<span class="st">'geometry_5km'</span>)</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
</div>
<p>Let’s revert to the original state of <code>seine</code> before moving on to the next section.</p>
<div id="0cd882e0" class="cell" data-execution_count="20">
<div class="sourceCode cell-code" id="cb20"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb20-1"><a href="#cb20-1" aria-hidden="true" tabindex="-1"></a>seine <span class="op">=</span> seine.set_geometry(<span class="st">'geometry'</span>)</span>
<span id="cb20-2"><a href="#cb20-2" aria-hidden="true" tabindex="-1"></a>seine <span class="op">=</span> seine.drop(<span class="st">'geometry_5km'</span>, axis<span class="op">=</span><span class="dv">1</span>)</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
</div>
</section>
<section id="sec-affine-transformations" class="level3" data-number="4.2.4">
<h3 data-number="4.2.4" class="anchored" data-anchor-id="sec-affine-transformations"><span class="header-section-number">4.2.4</span> Affine transformations</h3>
<p>Affine transformations include, among others, shifting (translation), scaling and rotation, or any combination of these. They preserves lines and parallelism, but angles and lengths are not necessarily preserved. These transformations are an essential part of geocomputation. For example, shifting is needed for labels placement, scaling is used in non-contiguous area cartograms, and many affine transformations are applied when reprojecting or improving the geometry that was created based on a distorted or wrongly projected map.</p>
<p>The <strong>geopandas</strong> package implements affine transformation, for objects of classes <code>GeoSeries</code> and <code>GeoDataFrame</code>. In both cases, the method is applied on the <code>GeoSeries</code> part, returning just the <code>GeoSeries</code> of transformed geometries.</p>
<p>Affine transformations of <code>GeoSeries</code> can be done using the <code>.affine_transform</code> method, which is a wrapper around the <code>shapely.affinity.affine_transform</code> function. A two-dimensional affine transformation requires a six-parameter list <code>[a,b,d,e,xoff,yoff]</code> which represents <a href="#eq-affine1" class="quarto-xref">Equation <span>4.1</span></a> and <a href="#eq-affine2" class="quarto-xref">Equation <span>4.2</span></a> for transforming the coordinates.</p>
<p><span id="eq-affine1"><span class="math display">\[
x' = a x + b y + x_\mathrm{off}
\tag{4.1}\]</span></span></p>
<p><span id="eq-affine2"><span class="math display">\[
y' = d x + e y + y_\mathrm{off}
\tag{4.2}\]</span></span></p>
<p>There are also simplified <code>GeoSeries</code> methods for specific scenarios, such as:</p>
<ul>
<li><code>.translate(xoff=0.0, yoff=0.0)</code></li>
<li><code>.scale(xfact=1.0, yfact=1.0, origin='center')</code></li>
<li><code>.rotate(angle, origin='center', use_radians=False)</code></li>
</ul>
<p>For example, <em>shifting</em> only requires the <span class="math inline">\(x_{off}\)</span> and <span class="math inline">\(y_{off}\)</span>, using <code>.translate</code>. The code below shifts the y-coordinates of <code>nz</code> by 100 <span class="math inline">\(km\)</span> to the north, but leaves the x-coordinates untouched.</p>
<div id="8a5a4225" class="cell" data-execution_count="21">
<div class="sourceCode cell-code" id="cb21"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb21-1"><a href="#cb21-1" aria-hidden="true" tabindex="-1"></a>nz_shift <span class="op">=</span> nz.translate(<span class="dv">0</span>, <span class="dv">100000</span>)</span>
<span id="cb21-2"><a href="#cb21-2" aria-hidden="true" tabindex="-1"></a>nz_shift</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<div class="cell-output cell-output-display" data-execution_count="21">
<pre><code>0 MULTIPOLYGON (((1745493.196 610...
1 MULTIPOLYGON (((1803822.103 600...
2 MULTIPOLYGON (((1860345.005 595...
...
13 MULTIPOLYGON (((1616642.877 552...
14 MULTIPOLYGON (((1624866.278 551...
15 MULTIPOLYGON (((1686901.914 545...
Length: 16, dtype: geometry</code></pre>
</div>
</div>
<div class="callout callout-style-default callout-note callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
Note
</div>
</div>
<div class="callout-body-container callout-body">
<p><strong>shapely</strong>, and consequently <strong>geopandas</strong>, operations, typically ignore the z-dimension (if there is one) of geometries in operations. For example, <code>shapely.LineString([(0,0,0),(0,0,1)]).length</code> returns <code>0</code> (and not <code>1</code>), since <code>.length</code> ignores the z-dimension. This is not an issue in this book (and in most real-world spatial analysis applications), since we are dealing only with two-dimensional geometries.</p>
</div>
</div>
<p>Scaling enlarges or shrinks objects by a factor, and can be applied either globally or locally. Global scaling increases or decreases all coordinates values in relation to the origin coordinates, while keeping all geometries topological relations intact. <strong>geopandas</strong> implements scaling using the <code>.scale</code> method. Local scaling treats geometries independently and requires points around which geometries are going to be scaled, e.g., centroids. In the example below, each geometry is shrunk by a factor of two around the centroids (<a href="#fig-affine-transformations" class="quarto-xref">Figure <span>4.5</span></a> (b)). To achieve that, we pass the <code>0.5</code> and <code>0.5</code> scaling factors (for x and y, respectively), and the <code>'centroid'</code> option for the point of origin.</p>
<div id="b19ce745" class="cell" data-execution_count="22">
<div class="sourceCode cell-code" id="cb23"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb23-1"><a href="#cb23-1" aria-hidden="true" tabindex="-1"></a>nz_scale <span class="op">=</span> nz.scale(<span class="fl">0.5</span>, <span class="fl">0.5</span>, origin<span class="op">=</span><span class="st">'centroid'</span>)</span>
<span id="cb23-2"><a href="#cb23-2" aria-hidden="true" tabindex="-1"></a>nz_scale</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<div class="cell-output cell-output-display" data-execution_count="22">
<pre><code>0 MULTIPOLYGON (((1710099.077 603...
1 MULTIPOLYGON (((1778686.524 591...
2 MULTIPOLYGON (((1839927.904 582...
...
13 MULTIPOLYGON (((1593619.59 5418...
14 MULTIPOLYGON (((1628907.395 542...
15 MULTIPOLYGON (((1665262.436 536...
Length: 16, dtype: geometry</code></pre>
</div>
</div>
<p>When setting the <code>origin</code> in <code>.scale</code>, other than <code>'centroid'</code> it is possible to use <code>'center'</code>, for the bounding box center, or specific point coordinates, such as <code>(0,0)</code>.</p>
<p>Rotating the geometries can be done using the <code>.rotate</code> method. When rotating, we need to specify the rotation angle (positive values imply clockwise rotation) and the <code>origin</code> points (using the same options as in <code>.scale</code>). For example, the following expression rotates <code>nz</code> by <span class="math inline">\(30\degree\)</span> counter-clockwise, around the geometry centroids.</p>
<div id="36ca8135" class="cell" data-execution_count="23">
<div class="sourceCode cell-code" id="cb25"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb25-1"><a href="#cb25-1" aria-hidden="true" tabindex="-1"></a>nz_rotate <span class="op">=</span> nz.rotate(<span class="op">-</span><span class="dv">30</span>, origin<span class="op">=</span><span class="st">'centroid'</span>)</span>
<span id="cb25-2"><a href="#cb25-2" aria-hidden="true" tabindex="-1"></a>nz_rotate</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<div class="cell-output cell-output-display" data-execution_count="23">
<pre><code>0 MULTIPOLYGON (((1701904.887 597...
1 MULTIPOLYGON (((1779714.772 587...
2 MULTIPOLYGON (((1890843.462 582...
...
13 MULTIPOLYGON (((1616991.636 539...
14 MULTIPOLYGON (((1617733.547 542...
15 MULTIPOLYGON (((1665898.669 533...
Length: 16, dtype: geometry</code></pre>
</div>
</div>
<p><a href="#fig-affine-transformations" class="quarto-xref">Figure <span>4.5</span></a> shows the original layer <code>nz</code>, and the shifting, scaling, and rotation results.</p>
<div class="sourceCode cell-code" id="cb27"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb27-1"><a href="#cb27-1" aria-hidden="true" tabindex="-1"></a><span class="co"># Shift</span></span>
<span id="cb27-2"><a href="#cb27-2" aria-hidden="true" tabindex="-1"></a>base <span class="op">=</span> nz.plot(color<span class="op">=</span><span class="st">'lightgrey'</span>, edgecolor<span class="op">=</span><span class="st">'darkgrey'</span>)</span>
<span id="cb27-3"><a href="#cb27-3" aria-hidden="true" tabindex="-1"></a>nz_shift.plot(ax<span class="op">=</span>base, color<span class="op">=</span><span class="st">'red'</span>, edgecolor<span class="op">=</span><span class="st">'darkgrey'</span>)<span class="op">;</span></span>
<span id="cb27-4"><a href="#cb27-4" aria-hidden="true" tabindex="-1"></a><span class="co"># Scale</span></span>
<span id="cb27-5"><a href="#cb27-5" aria-hidden="true" tabindex="-1"></a>base <span class="op">=</span> nz.plot(color<span class="op">=</span><span class="st">'lightgrey'</span>, edgecolor<span class="op">=</span><span class="st">'darkgrey'</span>)</span>
<span id="cb27-6"><a href="#cb27-6" aria-hidden="true" tabindex="-1"></a>nz_scale.plot(ax<span class="op">=</span>base, color<span class="op">=</span><span class="st">'red'</span>, edgecolor<span class="op">=</span><span class="st">'darkgrey'</span>)<span class="op">;</span></span>
<span id="cb27-7"><a href="#cb27-7" aria-hidden="true" tabindex="-1"></a><span class="co"># Rotate</span></span>
<span id="cb27-8"><a href="#cb27-8" aria-hidden="true" tabindex="-1"></a>base <span class="op">=</span> nz.plot(color<span class="op">=</span><span class="st">'lightgrey'</span>, edgecolor<span class="op">=</span><span class="st">'darkgrey'</span>)</span>
<span id="cb27-9"><a href="#cb27-9" aria-hidden="true" tabindex="-1"></a>nz_rotate.plot(ax<span class="op">=</span>base, color<span class="op">=</span><span class="st">'red'</span>, edgecolor<span class="op">=</span><span class="st">'darkgrey'</span>)<span class="op">;</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<div id="fig-affine-transformations" class="quarto-layout-panel">
<figure class="quarto-float quarto-float-fig figure">
<div aria-describedby="fig-affine-transformations-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
<div class="quarto-layout-row">
<div class="cell-output cell-output-display quarto-layout-cell-subref quarto-layout-cell" data-ref-parent="fig-affine-transformations" style="flex-basis: 33.3%;justify-content: flex-start;">
<div id="fig-affine-transformations-1" class="quarto-float quarto-figure quarto-figure-center anchored">
<figure class="quarto-float quarto-subfloat-fig figure">
<div aria-describedby="fig-affine-transformations-1-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
<img src="04-geometry-operations_files/figure-html/fig-affine-transformations-output-1.png" data-ref-parent="fig-affine-transformations" width="289" height="442" class="figure-img">
</div>
<figcaption class="quarto-float-caption-bottom quarto-subfloat-caption quarto-subfloat-fig" id="fig-affine-transformations-1-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
(a) Shift
</figcaption>
</figure>
</div>
</div>
<div class="cell-output cell-output-display quarto-layout-cell-subref quarto-layout-cell" data-ref-parent="fig-affine-transformations" style="flex-basis: 33.3%;justify-content: flex-start;">
<div id="fig-affine-transformations-2" class="quarto-float quarto-figure quarto-figure-center anchored">
<figure class="quarto-float quarto-subfloat-fig figure">
<div aria-describedby="fig-affine-transformations-2-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
<img src="04-geometry-operations_files/figure-html/fig-affine-transformations-output-2.png" data-ref-parent="fig-affine-transformations" width="306" height="442" class="figure-img">
</div>
<figcaption class="quarto-float-caption-bottom quarto-subfloat-caption quarto-subfloat-fig" id="fig-affine-transformations-2-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
(b) Scale
</figcaption>
</figure>
</div>
</div>
<div class="cell-output cell-output-display quarto-layout-cell-subref quarto-layout-cell" data-ref-parent="fig-affine-transformations" style="flex-basis: 33.3%;justify-content: flex-start;">
<div id="fig-affine-transformations-3" class="quarto-float quarto-figure quarto-figure-center anchored">
<figure class="quarto-float quarto-subfloat-fig figure">
<div aria-describedby="fig-affine-transformations-3-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
<img src="04-geometry-operations_files/figure-html/fig-affine-transformations-output-3.png" data-ref-parent="fig-affine-transformations" width="306" height="442" class="figure-img">
</div>
<figcaption class="quarto-float-caption-bottom quarto-subfloat-caption quarto-subfloat-fig" id="fig-affine-transformations-3-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
(c) Rotate
</figcaption>
</figure>
</div>
</div>
</div>
</div>
<figcaption class="quarto-float-caption-bottom quarto-float-caption quarto-float-fig" id="fig-affine-transformations-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
Figure 4.5: Affine transformations of the <code>nz</code> layer: shift, scale, and rotate
</figcaption>
</figure>
</div>
</section>
<section id="sec-clipping" class="level3" data-number="4.2.5">
<h3 data-number="4.2.5" class="anchored" data-anchor-id="sec-clipping"><span class="header-section-number">4.2.5</span> Pairwise geometry-generating operations</h3>
<p>Spatial clipping is a form of spatial subsetting that involves changes to the geometry columns of at least some of the affected features. Clipping can only apply to features more complex than points: lines, polygons, and their ‘multi’ equivalents. To illustrate the concept we will start with a simple example: two overlapping circles with a center point one unit away from each other and a radius of one (<a href="#fig-overlapping-circles" class="quarto-xref">Figure <span>4.6</span></a>).</p>
<div id="cell-fig-overlapping-circles" class="cell" data-execution_count="25">
<div class="sourceCode cell-code" id="cb28"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb28-1"><a href="#cb28-1" aria-hidden="true" tabindex="-1"></a>x <span class="op">=</span> shapely.Point((<span class="dv">0</span>, <span class="dv">0</span>)).<span class="bu">buffer</span>(<span class="dv">1</span>)</span>
<span id="cb28-2"><a href="#cb28-2" aria-hidden="true" tabindex="-1"></a>y <span class="op">=</span> shapely.Point((<span class="dv">1</span>, <span class="dv">0</span>)).<span class="bu">buffer</span>(<span class="dv">1</span>)</span>
<span id="cb28-3"><a href="#cb28-3" aria-hidden="true" tabindex="-1"></a>shapely.GeometryCollection([x, y])</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<div class="cell-output cell-output-display" data-execution_count="25">
<div id="fig-overlapping-circles" class="quarto-float quarto-figure quarto-figure-center anchored">
<figure class="quarto-float quarto-float-fig figure">
<div aria-describedby="fig-overlapping-circles-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
<img src="04-geometry-operations_files/figure-html/fig-overlapping-circles-output-1.svg" class="img-fluid figure-img">
</div>
<figcaption class="quarto-float-caption-bottom quarto-float-caption quarto-float-fig" id="fig-overlapping-circles-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
Figure 4.6: Overlapping polygon (circle) geometries <code>x</code> and <code>y</code>
</figcaption>
</figure>
</div>
</div>
</div>
<p>Imagine you want to select not one circle or the other, but the space covered by both <code>x</code> and <code>y</code>. This can be done using the <code>.intersection</code> method from <strong>shapely</strong>, illustrated using objects named <code>x</code> and <code>y</code> which represent the left- and right-hand circles (<a href="#fig-intersection" class="quarto-xref">Figure <span>4.7</span></a>).</p>
<div id="cell-fig-intersection" class="cell" data-execution_count="26">
<div class="sourceCode cell-code" id="cb29"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb29-1"><a href="#cb29-1" aria-hidden="true" tabindex="-1"></a>x.intersection(y)</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<div class="cell-output cell-output-display" data-execution_count="26">
<div id="fig-intersection" class="quarto-float quarto-figure quarto-figure-center anchored">
<figure class="quarto-float quarto-float-fig figure">
<div aria-describedby="fig-intersection-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
<img src="04-geometry-operations_files/figure-html/fig-intersection-output-1.svg" class="img-fluid figure-img">
</div>
<figcaption class="quarto-float-caption-bottom quarto-float-caption quarto-float-fig" id="fig-intersection-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
Figure 4.7: Intersection between <code>x</code> and <code>y</code>
</figcaption>
</figure>
</div>
</div>
</div>
<p>More generally, clipping is an example of a ‘pairwise geometry-generating operation’, where new geometries are generated from two inputs. Other than <code>.intersection</code> (<a href="#fig-intersection" class="quarto-xref">Figure <span>4.7</span></a>), there are three other standard pairwise operators: <code>.difference</code> (<a href="#fig-difference" class="quarto-xref">Figure <span>4.8</span></a>), <code>.union</code> (<a href="#fig-union" class="quarto-xref">Figure <span>4.9</span></a>), and <code>.symmetric_difference</code> (<a href="#fig-symmetric-difference" class="quarto-xref">Figure <span>4.10</span></a>).</p>
<div id="cell-fig-difference" class="cell" data-execution_count="27">
<div class="sourceCode cell-code" id="cb30"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb30-1"><a href="#cb30-1" aria-hidden="true" tabindex="-1"></a>x.difference(y)</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<div class="cell-output cell-output-display" data-execution_count="27">
<div id="fig-difference" class="quarto-float quarto-figure quarto-figure-center anchored">
<figure class="quarto-float quarto-float-fig figure">
<div aria-describedby="fig-difference-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
<img src="04-geometry-operations_files/figure-html/fig-difference-output-1.svg" class="img-fluid figure-img">
</div>
<figcaption class="quarto-float-caption-bottom quarto-float-caption quarto-float-fig" id="fig-difference-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
Figure 4.8: Difference between <code>x</code> and <code>y</code> (namely, <code>x</code> ‘minus’ <code>y</code>)
</figcaption>
</figure>
</div>
</div>
</div>
<div id="cell-fig-union" class="cell" data-execution_count="28">
<div class="sourceCode cell-code" id="cb31"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb31-1"><a href="#cb31-1" aria-hidden="true" tabindex="-1"></a>x.union(y)</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<div class="cell-output cell-output-display" data-execution_count="28">
<div id="fig-union" class="quarto-float quarto-figure quarto-figure-center anchored">
<figure class="quarto-float quarto-float-fig figure">
<div aria-describedby="fig-union-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
<img src="04-geometry-operations_files/figure-html/fig-union-output-1.svg" class="img-fluid figure-img">
</div>
<figcaption class="quarto-float-caption-bottom quarto-float-caption quarto-float-fig" id="fig-union-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
Figure 4.9: Union of <code>x</code> and <code>y</code>
</figcaption>
</figure>
</div>
</div>
</div>
<div id="cell-fig-symmetric-difference" class="cell" data-execution_count="29">
<div class="sourceCode cell-code" id="cb32"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb32-1"><a href="#cb32-1" aria-hidden="true" tabindex="-1"></a>x.symmetric_difference(y)</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<div class="cell-output cell-output-display" data-execution_count="29">
<div id="fig-symmetric-difference" class="quarto-float quarto-figure quarto-figure-center anchored">
<figure class="quarto-float quarto-float-fig figure">
<div aria-describedby="fig-symmetric-difference-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
<img src="04-geometry-operations_files/figure-html/fig-symmetric-difference-output-1.svg" class="img-fluid figure-img">
</div>
<figcaption class="quarto-float-caption-bottom quarto-float-caption quarto-float-fig" id="fig-symmetric-difference-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
Figure 4.10: Symmetric difference between <code>x</code> and <code>y</code>
</figcaption>
</figure>
</div>
</div>
</div>
<p>Keep in mind that <code>x</code> and <code>y</code> are interchangeable in all predicates except for <code>.difference</code>, where <code>x.difference(y)</code> means <code>x</code> minus <code>y</code>, whereas <code>y.difference(x)</code> means <code>y</code> minus <code>x</code>.</p>
<p>The latter examples demonstrate pairwise operations between individual <code>shapely</code> geometries. The <strong>geopandas</strong> package, as is often the case, contains wrappers of these <strong>shapely</strong> functions to be applied to multiple, or pairwise, use cases. For example, applying either of the pairwise methods on a <code>GeoSeries</code> or <code>GeoDataFrame</code>, combined with a <code>shapely</code> geometry, returns the pairwise (many-to-one) results (which is analogous to other operators, like <code>.intersects</code> or <code>.distance</code>, see <a href="03-spatial-operations.html#sec-spatial-subsetting-vector" class="quarto-xref"><span>Section 3.2.1</span></a> and <a href="03-spatial-operations.html#sec-distance-relations" class="quarto-xref"><span>Section 3.2.7</span></a>, respectively).</p>
<p>Let’s demonstrate the ‘many-to-one’ scenario by calculating the difference between each geometry in a <code>GeoSeries</code> and a fixed <code>shapely</code> geometry. To create the latter, let’s take <code>x</code> and combine it with itself translated (<a href="#sec-affine-transformations" class="quarto-xref"><span>Section 4.2.4</span></a>) to a distance of <code>1</code> and <code>2</code> units ‘upwards’ on the y-axis.</p>
<div id="72df5311" class="cell" data-execution_count="30">
<div class="sourceCode cell-code" id="cb33"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb33-1"><a href="#cb33-1" aria-hidden="true" tabindex="-1"></a>geom1 <span class="op">=</span> gpd.GeoSeries(x)</span>
<span id="cb33-2"><a href="#cb33-2" aria-hidden="true" tabindex="-1"></a>geom2 <span class="op">=</span> geom1.translate(<span class="dv">0</span>, <span class="dv">1</span>)</span>
<span id="cb33-3"><a href="#cb33-3" aria-hidden="true" tabindex="-1"></a>geom3 <span class="op">=</span> geom1.translate(<span class="dv">0</span>, <span class="dv">2</span>)</span>
<span id="cb33-4"><a href="#cb33-4" aria-hidden="true" tabindex="-1"></a>geom <span class="op">=</span> pd.concat([geom1, geom2, geom3])</span>
<span id="cb33-5"><a href="#cb33-5" aria-hidden="true" tabindex="-1"></a>geom</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<div class="cell-output cell-output-display" data-execution_count="30">
<pre><code>0 POLYGON ((1 0, 0.99518 -0.09802...
0 POLYGON ((1 1, 0.99518 0.90198,...
0 POLYGON ((1 2, 0.99518 1.90198,...
dtype: geometry</code></pre>
</div>
</div>
<p><a href="#fig-geom-intersection" class="quarto-xref">Figure <span>4.11</span></a> shows the <code>GeoSeries</code> <code>geom</code> with the <code>shapely</code> geometry (in red) that we will intersect with it.</p>
<div id="cell-fig-geom-intersection" class="cell" data-execution_count="31">
<div class="sourceCode cell-code" id="cb35"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb35-1"><a href="#cb35-1" aria-hidden="true" tabindex="-1"></a>fig, ax <span class="op">=</span> plt.subplots()</span>
<span id="cb35-2"><a href="#cb35-2" aria-hidden="true" tabindex="-1"></a>geom.plot(color<span class="op">=</span><span class="st">'#00000030'</span>, edgecolor<span class="op">=</span><span class="st">'black'</span>, ax<span class="op">=</span>ax)</span>
<span id="cb35-3"><a href="#cb35-3" aria-hidden="true" tabindex="-1"></a>gpd.GeoSeries(y).plot(color<span class="op">=</span><span class="st">'#FF000040'</span>, edgecolor<span class="op">=</span><span class="st">'black'</span>, ax<span class="op">=</span>ax)<span class="op">;</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<div class="cell-output cell-output-display">
<div id="fig-geom-intersection" class="quarto-float quarto-figure quarto-figure-center anchored">
<figure class="quarto-float quarto-float-fig figure">
<div aria-describedby="fig-geom-intersection-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
<img src="04-geometry-operations_files/figure-html/fig-geom-intersection-output-1.png" width="338" height="411" class="figure-img">
</div>
<figcaption class="quarto-float-caption-bottom quarto-float-caption quarto-float-fig" id="fig-geom-intersection-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
Figure 4.11: A <code>GeoSeries</code> with three circles (in grey), and a <code>shapely</code> geometry that we will subtract from it (in red)
</figcaption>
</figure>
</div>
</div>
</div>
<p>Now, using <code>.intersection</code> automatically applies the <strong>shapely</strong> method of the same name on each geometry in <code>geom</code>, returning a new <code>GeoSeries</code>, which we name <code>geom_inter_y</code>, with the pairwise intersections. Note the empty third geometry (can you explain the meaning of this result?).</p>
<div id="5e97de71" class="cell" data-execution_count="32">
<div class="sourceCode cell-code" id="cb36"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb36-1"><a href="#cb36-1" aria-hidden="true" tabindex="-1"></a>geom_inter_y <span class="op">=</span> geom.intersection(y)</span>
<span id="cb36-2"><a href="#cb36-2" aria-hidden="true" tabindex="-1"></a>geom_inter_y</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<div class="cell-output cell-output-display" data-execution_count="32">
<pre><code>0 POLYGON ((0.99518 -0.09802, 0.9...
0 POLYGON ((0.99518 0.90198, 0.98...
0 POLYGON EMPTY
dtype: geometry</code></pre>
</div>
</div>
<p><a href="#fig-geom-intersection2" class="quarto-xref">Figure <span>4.12</span></a> is a plot of the result <code>geom_inter_y</code>.</p>
<div id="cell-fig-geom-intersection2" class="cell" data-execution_count="33">
<div class="sourceCode cell-code" id="cb38"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb38-1"><a href="#cb38-1" aria-hidden="true" tabindex="-1"></a>geom_inter_y.plot(color<span class="op">=</span><span class="st">'#00000030'</span>, edgecolor<span class="op">=</span><span class="st">'black'</span>)<span class="op">;</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<div class="cell-output cell-output-display">
<div id="fig-geom-intersection2" class="quarto-float quarto-figure quarto-figure-center anchored">
<figure class="quarto-float quarto-float-fig figure">
<div aria-describedby="fig-geom-intersection2-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
<img src="04-geometry-operations_files/figure-html/fig-geom-intersection2-output-1.png" width="269" height="411" class="figure-img">
</div>
<figcaption class="quarto-float-caption-bottom quarto-float-caption quarto-float-fig" id="fig-geom-intersection2-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
Figure 4.12: The output <code>GeoSeries</code>, after subtracting a <code>shapely</code> geometry using <code>.intersection</code>
</figcaption>
</figure>
</div>
</div>
</div>
<p>The <code>.overlay</code> method (see <a href="03-spatial-operations.html#sec-joining-incongruent-layers" class="quarto-xref"><span>Section 3.2.6</span></a>) further extends this technique, making it possible to apply ‘many-to-many’ pairwise geometry generations between all pairs of two <code>GeoDataFrame</code>s. The output is a new <code>GeoDataFrame</code> with the pairwise outputs, plus the attributes of both inputs which were the inputs of the particular pairwise output geometry. Also see the <em>Set operations with overlay</em><a href="#fn1" class="footnote-ref" id="fnref1" role="doc-noteref"><sup>1</sup></a> article in the <strong>geopandas</strong> documentation for examples of <code>.overlay</code>.</p>
</section>
<section id="sec-subsetting-vs-clipping" class="level3" data-number="4.2.6">
<h3 data-number="4.2.6" class="anchored" data-anchor-id="sec-subsetting-vs-clipping"><span class="header-section-number">4.2.6</span> Subsetting vs. clipping</h3>
<p>In the last two chapters we have introduced two types of spatial operators: boolean, such as <code>.intersects</code> (<a href="03-spatial-operations.html#sec-spatial-subsetting-vector" class="quarto-xref"><span>Section 3.2.1</span></a>), and geometry-generating, such as <code>.intersection</code> (<a href="#sec-clipping" class="quarto-xref"><span>Section 4.2.5</span></a>). Here, we illustrate the difference between them. We do this using the specific scenario of subsetting points by polygons, where (unlike in other cases) both methods can be used for the same purpose and giving the same result.</p>