Some basic exploration into the generic point in... style questions.
A recent comment about Select By Location taking a long time when point sets were large got me thinking. The background on this topic generally leave one staring a reams of code with little to no explanation on the logic. I will continue with that trend
.
First the code. I use python and numpy to do a lot of the work and we will use arcpy to communicate with ArcMap or ArcGIS Pro.
I created a featureclass in a file geodatabase of points, and a polygon representing a selection polygon. The following script was used to generate the data prior to testing the code. The sample was small for now, 10,000 points and one polygon (square). More on scaling at a later time.
Explore.
<SPAN class="comment token"># -*- coding: UTF-8 -*-</SPAN>
<SPAN class="string token">""</SPAN>"
<SPAN class="punctuation token">:</SPAN>Script<SPAN class="punctuation token">:</SPAN> point_in_rect
<SPAN class="punctuation token">:</SPAN>Author<SPAN class="punctuation token">:</SPAN> Dan<SPAN class="punctuation token">.</SPAN>Patterson@carleton<SPAN class="punctuation token">.</SPAN>ca
<SPAN class="punctuation token">:</SPAN>Purpose<SPAN class="punctuation token">:</SPAN>
<SPAN class="punctuation token">:</SPAN> To determine whether points are within the extents of polygons<SPAN class="punctuation token">.</SPAN>
<SPAN class="punctuation token">:</SPAN>
<SPAN class="punctuation token">:</SPAN>References<SPAN class="punctuation token">:</SPAN>
<SPAN class="punctuation token">:</SPAN> http<SPAN class="punctuation token">:</SPAN><SPAN class="operator token">//</SPAN>stackoverflow<SPAN class="punctuation token">.</SPAN>com<SPAN class="operator token">/</SPAN>questions<SPAN class="operator token">/</SPAN><SPAN class="number token">30481577</SPAN><SPAN class="operator token">/</SPAN>
<SPAN class="punctuation token">:</SPAN> assign<SPAN class="operator token">-</SPAN>numpy<SPAN class="operator token">-</SPAN>array<SPAN class="operator token">-</SPAN>of<SPAN class="operator token">-</SPAN>points<SPAN class="operator token">-</SPAN>to<SPAN class="operator token">-</SPAN>a<SPAN class="number token">-2d</SPAN><SPAN class="operator token">-</SPAN>square<SPAN class="operator token">-</SPAN>grid
<SPAN class="punctuation token">:</SPAN> http<SPAN class="punctuation token">:</SPAN><SPAN class="operator token">//</SPAN>stackoverflow<SPAN class="punctuation token">.</SPAN>com<SPAN class="operator token">/</SPAN>questions<SPAN class="operator token">/</SPAN><SPAN class="number token">33051244</SPAN><SPAN class="operator token">/</SPAN>
<SPAN class="punctuation token">:</SPAN> numpy<SPAN class="operator token">-</SPAN>filter<SPAN class="operator token">-</SPAN>points<SPAN class="operator token">-</SPAN>within<SPAN class="operator token">-</SPAN>bounding<SPAN class="operator token">-</SPAN>box
<SPAN class="punctuation token">:</SPAN> https<SPAN class="punctuation token">:</SPAN><SPAN class="operator token">//</SPAN>stackoverflow<SPAN class="punctuation token">.</SPAN>com<SPAN class="operator token">/</SPAN>questions<SPAN class="operator token">/</SPAN><SPAN class="number token">33051244</SPAN><SPAN class="operator token">/</SPAN>
<SPAN class="punctuation token">:</SPAN> numpy<SPAN class="operator token">-</SPAN>filter<SPAN class="operator token">-</SPAN>points<SPAN class="operator token">-</SPAN>within<SPAN class="operator token">-</SPAN>bounding<SPAN class="operator token">-</SPAN>box<SPAN class="operator token">/</SPAN><SPAN class="number token">33051576</SPAN><SPAN class="comment token">#33051576</SPAN>
<SPAN class="punctuation token">:</SPAN>
<SPAN class="string token">""</SPAN>"
<SPAN class="comment token"># ----10| ------20| ------30| ------40| ------50| ------60| ------70| ------80|</SPAN>
<SPAN class="keyword token">import</SPAN> numpy <SPAN class="keyword token">as</SPAN> np
<SPAN class="keyword token">import</SPAN> arcpy
np<SPAN class="punctuation token">.</SPAN>set_printoptions<SPAN class="punctuation token">(</SPAN>edgeitems<SPAN class="operator token">=</SPAN><SPAN class="number token">5</SPAN><SPAN class="punctuation token">,</SPAN> linewidth<SPAN class="operator token">=</SPAN><SPAN class="number token">80</SPAN><SPAN class="punctuation token">,</SPAN> precision<SPAN class="operator token">=</SPAN><SPAN class="number token">4</SPAN><SPAN class="punctuation token">,</SPAN>
suppress<SPAN class="operator token">=</SPAN><SPAN class="token boolean">True</SPAN><SPAN class="punctuation token">,</SPAN> threshold<SPAN class="operator token">=</SPAN><SPAN class="number token">20</SPAN><SPAN class="punctuation token">)</SPAN>
<SPAN class="keyword token">def</SPAN> <SPAN class="token function">contains</SPAN><SPAN class="punctuation token">(</SPAN>pnts<SPAN class="punctuation token">,</SPAN> ext<SPAN class="punctuation token">,</SPAN> in_out<SPAN class="operator token">=</SPAN><SPAN class="token boolean">True</SPAN><SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">:</SPAN>
<SPAN class="string token">""</SPAN>"Performs a logical_and to find points within a box<SPAN class="operator token">/</SPAN>ext
<SPAN class="punctuation token">:</SPAN>Requires<SPAN class="punctuation token">:</SPAN>
<SPAN class="punctuation token">:</SPAN><SPAN class="operator token">-</SPAN><SPAN class="operator token">-</SPAN><SPAN class="operator token">-</SPAN><SPAN class="operator token">-</SPAN><SPAN class="operator token">-</SPAN><SPAN class="operator token">-</SPAN><SPAN class="operator token">-</SPAN><SPAN class="operator token">-</SPAN>
<SPAN class="punctuation token">:</SPAN> pnts <SPAN class="operator token">-</SPAN> an array of points
<SPAN class="punctuation token">:</SPAN> ext <SPAN class="operator token">-</SPAN> the extent of the rectangle being tested <SPAN class="keyword token">as</SPAN> an array of the
<SPAN class="punctuation token">:</SPAN> left bottom <SPAN class="punctuation token">(</SPAN>LB<SPAN class="punctuation token">)</SPAN> <SPAN class="operator token">and</SPAN> upper right <SPAN class="punctuation token">(</SPAN>RT<SPAN class="punctuation token">)</SPAN> coordinates
<SPAN class="punctuation token">:</SPAN> in_out <SPAN class="operator token">-</SPAN> boolean<SPAN class="punctuation token">,</SPAN> whether to <SPAN class="keyword token">return</SPAN> inside <SPAN class="operator token">and</SPAN> outside points
<SPAN class="punctuation token">:</SPAN>
<SPAN class="punctuation token">:</SPAN>Notes<SPAN class="punctuation token">:</SPAN>
<SPAN class="punctuation token">:</SPAN><SPAN class="operator token">-</SPAN><SPAN class="operator token">-</SPAN><SPAN class="operator token">-</SPAN><SPAN class="operator token">-</SPAN><SPAN class="operator token">-</SPAN>
<SPAN class="punctuation token">:</SPAN> comp <SPAN class="operator token">-</SPAN> np<SPAN class="punctuation token">.</SPAN>logical_and<SPAN class="punctuation token">(</SPAN> great<SPAN class="operator token">-</SPAN>eq LB<SPAN class="punctuation token">,</SPAN> less RT<SPAN class="punctuation token">)</SPAN> <SPAN class="comment token"># logic</SPAN>
<SPAN class="punctuation token">:</SPAN> inside <SPAN class="operator token">-</SPAN> np<SPAN class="punctuation token">.</SPAN>where<SPAN class="punctuation token">(</SPAN>np<SPAN class="punctuation token">.</SPAN>prod<SPAN class="punctuation token">(</SPAN>comp<SPAN class="punctuation token">,</SPAN> axis<SPAN class="operator token">=</SPAN><SPAN class="number token">1</SPAN><SPAN class="punctuation token">)</SPAN> <SPAN class="operator token">==</SPAN> <SPAN class="number token">1</SPAN><SPAN class="punctuation token">)</SPAN> <SPAN class="comment token"># logic</SPAN>
<SPAN class="punctuation token">:</SPAN> case <SPAN class="operator token">-</SPAN> comp returns <SPAN class="punctuation token">[</SPAN><SPAN class="token boolean">True</SPAN><SPAN class="punctuation token">,</SPAN> <SPAN class="token boolean">False</SPAN><SPAN class="punctuation token">]</SPAN> so you take the product
<SPAN class="punctuation token">:</SPAN> idx_in <SPAN class="operator token">-</SPAN> indices derived using where since case will be <SPAN class="number token">0</SPAN> <SPAN class="operator token">or</SPAN> <SPAN class="number token">1</SPAN>
<SPAN class="punctuation token">:</SPAN> inside <SPAN class="operator token">-</SPAN> slice the pnts using idx_in
<SPAN class="string token">""</SPAN>"
outside <SPAN class="operator token">=</SPAN> None
LB<SPAN class="punctuation token">,</SPAN> RT <SPAN class="operator token">=</SPAN> ext
comp <SPAN class="operator token">=</SPAN> np<SPAN class="punctuation token">.</SPAN>logical_and<SPAN class="punctuation token">(</SPAN><SPAN class="punctuation token">(</SPAN>LB <SPAN class="operator token"><=</SPAN> pnts<SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">,</SPAN> <SPAN class="punctuation token">(</SPAN>pnts <SPAN class="operator token"><=</SPAN> RT<SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">)</SPAN>
case <SPAN class="operator token">=</SPAN> comp<SPAN class="punctuation token">[</SPAN><SPAN class="punctuation token">:</SPAN><SPAN class="punctuation token">,</SPAN> <SPAN class="number token">0</SPAN><SPAN class="punctuation token">]</SPAN> <SPAN class="operator token">*</SPAN> comp<SPAN class="punctuation token">[</SPAN><SPAN class="punctuation token">:</SPAN><SPAN class="punctuation token">,</SPAN> <SPAN class="number token">1</SPAN><SPAN class="punctuation token">]</SPAN>
idx_in <SPAN class="operator token">=</SPAN> np<SPAN class="punctuation token">.</SPAN>where<SPAN class="punctuation token">(</SPAN>case<SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">[</SPAN><SPAN class="number token">0</SPAN><SPAN class="punctuation token">]</SPAN>
inside <SPAN class="operator token">=</SPAN> pnts<SPAN class="punctuation token">[</SPAN>idx_in<SPAN class="punctuation token">]</SPAN>
<SPAN class="keyword token">if</SPAN> in_out<SPAN class="punctuation token">:</SPAN>
idx_out <SPAN class="operator token">=</SPAN> np<SPAN class="punctuation token">.</SPAN>where<SPAN class="punctuation token">(</SPAN><SPAN class="operator token">~</SPAN>case<SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">[</SPAN><SPAN class="number token">0</SPAN><SPAN class="punctuation token">]</SPAN> <SPAN class="comment token"># invert case</SPAN>
outside <SPAN class="operator token">=</SPAN> pnts<SPAN class="punctuation token">[</SPAN>idx_out<SPAN class="punctuation token">]</SPAN>
<SPAN class="keyword token">return</SPAN> inside<SPAN class="punctuation token">,</SPAN> outside
<SPAN class="keyword token">if</SPAN> __name__ <SPAN class="operator token">==</SPAN> <SPAN class="string token">"__main__"</SPAN><SPAN class="punctuation token">:</SPAN>
<SPAN class="string token">"""make some points for testing, create and extent,
"""</SPAN>
ext <SPAN class="operator token">=</SPAN> np<SPAN class="punctuation token">.</SPAN>array<SPAN class="punctuation token">(</SPAN><SPAN class="punctuation token">[</SPAN><SPAN class="punctuation token">[</SPAN><SPAN class="number token">342000</SPAN><SPAN class="punctuation token">,</SPAN> <SPAN class="number token">5022000</SPAN><SPAN class="punctuation token">]</SPAN><SPAN class="punctuation token">,</SPAN> <SPAN class="punctuation token">[</SPAN><SPAN class="number token">343000</SPAN><SPAN class="punctuation token">,</SPAN> <SPAN class="number token">5023000</SPAN><SPAN class="punctuation token">]</SPAN><SPAN class="punctuation token">]</SPAN><SPAN class="punctuation token">)</SPAN>
in_fc <SPAN class="operator token">=</SPAN> r<SPAN class="string token">'C:\Git_Dan\a_Data\testdata.gdb\xy_10k'</SPAN>
SR <SPAN class="operator token">=</SPAN> arcpy<SPAN class="punctuation token">.</SPAN>SpatialReference<SPAN class="punctuation token">(</SPAN><SPAN class="number token">2951</SPAN><SPAN class="punctuation token">)</SPAN>
a <SPAN class="operator token">=</SPAN> arcpy<SPAN class="punctuation token">.</SPAN>da<SPAN class="punctuation token">.</SPAN>FeatureClassToNumPyArray<SPAN class="punctuation token">(</SPAN>in_fc<SPAN class="punctuation token">,</SPAN>
<SPAN class="punctuation token">[</SPAN><SPAN class="string token">'SHAPE@X'</SPAN><SPAN class="punctuation token">,</SPAN> <SPAN class="string token">'SHAPE@Y'</SPAN><SPAN class="punctuation token">]</SPAN><SPAN class="punctuation token">,</SPAN>
spatial_reference<SPAN class="operator token">=</SPAN>SR<SPAN class="punctuation token">)</SPAN>
a0 <SPAN class="operator token">=</SPAN> a<SPAN class="punctuation token">.</SPAN>view<SPAN class="punctuation token">(</SPAN>dtype<SPAN class="operator token">=</SPAN>np<SPAN class="punctuation token">.</SPAN>float<SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">.</SPAN>reshape<SPAN class="punctuation token">(</SPAN>len<SPAN class="punctuation token">(</SPAN>a<SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">,</SPAN> <SPAN class="number token">2</SPAN><SPAN class="punctuation token">)</SPAN>
arcpy<SPAN class="punctuation token">.</SPAN>env<SPAN class="punctuation token">.</SPAN>overwriteOutput <SPAN class="operator token">=</SPAN> <SPAN class="token boolean">True</SPAN>
arcpy<SPAN class="punctuation token">.</SPAN>env<SPAN class="punctuation token">.</SPAN>workspace <SPAN class="operator token">=</SPAN> r<SPAN class="string token">'C:\Git_Dan\a_Data\testdata.gdb'</SPAN>
arcpy<SPAN class="punctuation token">.</SPAN>MakeFeatureLayer_management<SPAN class="punctuation token">(</SPAN><SPAN class="string token">'xy_10k'</SPAN><SPAN class="punctuation token">,</SPAN> <SPAN class="string token">'xy_lyr'</SPAN><SPAN class="punctuation token">)</SPAN>
<SPAN class="comment token">#arcpy.MakeFeatureLayer_management('subpoly', 'subpoly_lyr')</SPAN>
arcpy<SPAN class="punctuation token">.</SPAN>management<SPAN class="punctuation token">.</SPAN>SelectLayerByLocation<SPAN class="punctuation token">(</SPAN><SPAN class="string token">"xy_lyr"</SPAN><SPAN class="punctuation token">,</SPAN> <SPAN class="string token">"INTERSECT"</SPAN><SPAN class="punctuation token">,</SPAN> <SPAN class="string token">"subpoly"</SPAN><SPAN class="punctuation token">,</SPAN>
None<SPAN class="punctuation token">,</SPAN> <SPAN class="string token">"NEW_SELECTION"</SPAN><SPAN class="punctuation token">,</SPAN> <SPAN class="string token">"NOT_INVERT"</SPAN><SPAN class="punctuation token">)</SPAN>
matchcount <SPAN class="operator token">=</SPAN> int<SPAN class="punctuation token">(</SPAN>arcpy<SPAN class="punctuation token">.</SPAN>GetCount_management<SPAN class="punctuation token">(</SPAN><SPAN class="string token">'xy_lyr'</SPAN><SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">[</SPAN><SPAN class="number token">0</SPAN><SPAN class="punctuation token">]</SPAN><SPAN class="punctuation token">)</SPAN><SPAN class="line-numbers-rows"><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN></SPAN>The results are shown in the figure below.

In this example, the script was run as it was, then the IPython magic %timeit was used to time just the portions that didn't require data preparation.
In both cases, 401 points were returned from a featureclass of 10,000 points. The timing results are below.
Timing Results
(1) Numpy
Timing contains with the array created from the points.
<SPAN class="operator token">%</SPAN>timeit contains<SPAN class="punctuation token">(</SPAN>a0<SPAN class="punctuation token">,</SPAN> ext<SPAN class="punctuation token">,</SPAN> in_out<SPAN class="operator token">=</SPAN><SPAN class="token boolean">False</SPAN><SPAN class="punctuation token">)</SPAN>
<SPAN class="number token">195</SPAN> µs ± <SPAN class="number token">11.1</SPAN> µs per loop <SPAN class="punctuation token">(</SPAN>mean ± std<SPAN class="punctuation token">.</SPAN> dev<SPAN class="punctuation token">.</SPAN> of <SPAN class="number token">7</SPAN> runs<SPAN class="punctuation token">,</SPAN> <SPAN class="number token">1000</SPAN> loops each<SPAN class="punctuation token">)</SPAN><SPAN class="line-numbers-rows"><SPAN></SPAN><SPAN></SPAN></SPAN>
(2) ArcGIS Pro and SelectByLocation
Just timing the final SelectLayerByLocation
<SPAN class="operator token">%</SPAN>timeit arcpy<SPAN class="punctuation token">.</SPAN>management<SPAN class="punctuation token">.</SPAN>SelectLayerByLocation<SPAN class="punctuation token">(</SPAN><SPAN class="string token">"xy_lyr"</SPAN><SPAN class="punctuation token">,</SPAN> <SPAN class="string token">"INTERSECT"</SPAN><SPAN class="punctuation token">,</SPAN> <SPAN class="string token">"subpoly"</SPAN><SPAN class="punctuation token">,</SPAN> None<SPAN class="punctuation token">,</SPAN> <SPAN class="string token">"NEW_SELECTION"</SPAN><SPAN class="punctuation token">,</SPAN> <SPAN class="string token">"NOT_INVERT"</SPAN><SPAN class="punctuation token">)</SPAN>
<SPAN class="number token">308</SPAN> ms ± <SPAN class="number token">11.7</SPAN> ms per loop <SPAN class="punctuation token">(</SPAN>mean ± std<SPAN class="punctuation token">.</SPAN> dev<SPAN class="punctuation token">.</SPAN> of <SPAN class="number token">7</SPAN> runs<SPAN class="punctuation token">,</SPAN> <SPAN class="number token">1</SPAN> loop each<SPAN class="punctuation token">)</SPAN><SPAN class="line-numbers-rows"><SPAN></SPAN><SPAN></SPAN></SPAN>
(3) arcpy in ArcGIS Pro
In this incarnation, I use the same files and extract the geometry from them and perform a point 'within' polygon. The following shows the appropriate additions.
<SPAN class="comment token"># imports as in previous script</SPAN>
<SPAN class="keyword token">def</SPAN> <SPAN class="token function">src_geom</SPAN><SPAN class="punctuation token">(</SPAN>pnt_fc<SPAN class="punctuation token">,</SPAN> poly_fc<SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">:</SPAN>
<SPAN class="string token">""" a search cursor approach
"""</SPAN>
pnts <SPAN class="operator token">=</SPAN> <SPAN class="punctuation token">[</SPAN>p<SPAN class="punctuation token">[</SPAN><SPAN class="number token">0</SPAN><SPAN class="punctuation token">]</SPAN> <SPAN class="keyword token">for</SPAN> p <SPAN class="keyword token">in</SPAN> arcpy<SPAN class="punctuation token">.</SPAN>da<SPAN class="punctuation token">.</SPAN>SearchCursor<SPAN class="punctuation token">(</SPAN>pnt_fc<SPAN class="punctuation token">,</SPAN> <SPAN class="string token">"SHAPE@"</SPAN><SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">]</SPAN>
polys <SPAN class="operator token">=</SPAN> <SPAN class="punctuation token">[</SPAN>pl<SPAN class="punctuation token">[</SPAN><SPAN class="number token">0</SPAN><SPAN class="punctuation token">]</SPAN> <SPAN class="keyword token">for</SPAN> pl <SPAN class="keyword token">in</SPAN> arcpy<SPAN class="punctuation token">.</SPAN>da<SPAN class="punctuation token">.</SPAN>SearchCursor<SPAN class="punctuation token">(</SPAN>poly_fc<SPAN class="punctuation token">,</SPAN> <SPAN class="string token">"SHAPE@"</SPAN><SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">]</SPAN>
poly <SPAN class="operator token">=</SPAN> polys<SPAN class="punctuation token">[</SPAN><SPAN class="number token">0</SPAN><SPAN class="punctuation token">]</SPAN>
is_in <SPAN class="operator token">=</SPAN> <SPAN class="punctuation token">[</SPAN>pnt<SPAN class="punctuation token">.</SPAN>within<SPAN class="punctuation token">(</SPAN>poly<SPAN class="punctuation token">)</SPAN> <SPAN class="keyword token">for</SPAN> pnt <SPAN class="keyword token">in</SPAN> pnts<SPAN class="punctuation token">]</SPAN>
<SPAN class="keyword token">return</SPAN> pnts<SPAN class="punctuation token">,</SPAN> poly<SPAN class="punctuation token">,</SPAN> is_in
<SPAN class="keyword token">if</SPAN> __name__ <SPAN class="operator token">==</SPAN> <SPAN class="string token">"__main__"</SPAN><SPAN class="punctuation token">:</SPAN>
<SPAN class="string token">"""searchcursor, arcpy point within polygon approach
"""</SPAN>
arcpy<SPAN class="punctuation token">.</SPAN>env<SPAN class="punctuation token">.</SPAN>overwriteOutput <SPAN class="operator token">=</SPAN> <SPAN class="token boolean">True</SPAN>
arcpy<SPAN class="punctuation token">.</SPAN>env<SPAN class="punctuation token">.</SPAN>workspace <SPAN class="operator token">=</SPAN> r<SPAN class="string token">'C:\Git_Dan\a_Data\testdata.gdb'</SPAN>
pnt_fc <SPAN class="operator token">=</SPAN> r<SPAN class="string token">'C:\Git_Dan\a_Data\testdata.gdb\xy_10k'</SPAN> <SPAN class="comment token"># the point layer as before</SPAN>
poly_fc <SPAN class="operator token">=</SPAN> r<SPAN class="string token">'C:\Git_Dan\a_Data\testdata.gdb\subpoly'</SPAN> <SPAN class="comment token"># a single polygon</SPAN>
<SPAN class="comment token"># ---- </SPAN>
pnts<SPAN class="punctuation token">,</SPAN> poly<SPAN class="punctuation token">,</SPAN> is_in <SPAN class="operator token">=</SPAN> src_geom<SPAN class="punctuation token">(</SPAN>pnt_fc<SPAN class="punctuation token">,</SPAN> poly_fc<SPAN class="punctuation token">)</SPAN>
pnts_in <SPAN class="operator token">=</SPAN> sum<SPAN class="punctuation token">(</SPAN>is_in<SPAN class="punctuation token">)</SPAN>
<SPAN class="comment token"># ----</SPAN><SPAN class="line-numbers-rows"><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN></SPAN>
The result yielded the same number of points ( ie pnts_in = 401)
<SPAN class="operator token">%</SPAN>timeit src_geom<SPAN class="punctuation token">(</SPAN>pnt_fc<SPAN class="punctuation token">,</SPAN> poly_fc<SPAN class="punctuation token">)</SPAN>
<SPAN class="number token">472</SPAN> ms ± <SPAN class="number token">2.57</SPAN> ms per loop <SPAN class="punctuation token">(</SPAN>mean ± std<SPAN class="punctuation token">.</SPAN> dev<SPAN class="punctuation token">.</SPAN> of <SPAN class="number token">7</SPAN> runs<SPAN class="punctuation token">,</SPAN> <SPAN class="number token">1</SPAN> loop each<SPAN class="punctuation token">)</SPAN><SPAN class="line-numbers-rows"><SPAN></SPAN><SPAN></SPAN></SPAN>
(4) MatPlotLib and Path.contains_point
MatPlotLib's 'path' module can be used to implement point in polygon testing. Since it is available within the Anaconda suite, then it is included here for comparison purposes.
<SPAN class="keyword token">from</SPAN> matplotlib<SPAN class="punctuation token">.</SPAN>path <SPAN class="keyword token">import</SPAN> Path
pip <SPAN class="operator token">=</SPAN> Path<SPAN class="punctuation token">.</SPAN>contains_point <SPAN class="comment token"># ---- short form of method ----</SPAN>
<SPAN class="keyword token">def</SPAN> <SPAN class="token function">_extent</SPAN><SPAN class="punctuation token">(</SPAN>ext<SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">:</SPAN>
<SPAN class="string token">"""construct the extent rectangle from the extent points which are the
: lower left and upper right points [LB, RT]
"""</SPAN>
LB<SPAN class="punctuation token">,</SPAN> RT <SPAN class="operator token">=</SPAN> ext
L<SPAN class="punctuation token">,</SPAN> B <SPAN class="operator token">=</SPAN> LB
R<SPAN class="punctuation token">,</SPAN> T <SPAN class="operator token">=</SPAN> RT
box <SPAN class="operator token">=</SPAN> <SPAN class="punctuation token">[</SPAN>LB<SPAN class="punctuation token">,</SPAN> <SPAN class="punctuation token">[</SPAN>L<SPAN class="punctuation token">,</SPAN> T<SPAN class="punctuation token">]</SPAN><SPAN class="punctuation token">,</SPAN> RT<SPAN class="punctuation token">,</SPAN> <SPAN class="punctuation token">[</SPAN>R<SPAN class="punctuation token">,</SPAN> B<SPAN class="punctuation token">]</SPAN><SPAN class="punctuation token">,</SPAN> LB<SPAN class="punctuation token">]</SPAN>
ext_rect <SPAN class="operator token">=</SPAN> np<SPAN class="punctuation token">.</SPAN>array<SPAN class="punctuation token">(</SPAN>box<SPAN class="punctuation token">)</SPAN>
<SPAN class="keyword token">return</SPAN> ext_rect
<SPAN class="keyword token">def</SPAN> <SPAN class="token function">containsmpl</SPAN><SPAN class="punctuation token">(</SPAN>pnts<SPAN class="punctuation token">,</SPAN> ext<SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">:</SPAN>
<SPAN class="string token">"""matplotlib incarnation
"""</SPAN>
poly <SPAN class="operator token">=</SPAN> _extent<SPAN class="punctuation token">(</SPAN>ext<SPAN class="punctuation token">)</SPAN>
poly <SPAN class="operator token">=</SPAN> matplotlib<SPAN class="punctuation token">.</SPAN>path<SPAN class="punctuation token">.</SPAN>Path<SPAN class="punctuation token">(</SPAN>poly<SPAN class="punctuation token">)</SPAN>
is_in <SPAN class="operator token">=</SPAN> <SPAN class="punctuation token">[</SPAN>pip<SPAN class="punctuation token">(</SPAN>poly<SPAN class="punctuation token">,</SPAN> p<SPAN class="punctuation token">)</SPAN> <SPAN class="keyword token">for</SPAN> p <SPAN class="keyword token">in</SPAN> pnts<SPAN class="punctuation token">]</SPAN>
<SPAN class="keyword token">return</SPAN> np<SPAN class="punctuation token">.</SPAN>array<SPAN class="punctuation token">(</SPAN>is_in<SPAN class="punctuation token">)</SPAN><SPAN class="line-numbers-rows"><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN></SPAN>
Using the numpy array objects for the points and the polygon, as inputs, the results are as follows:
<SPAN class="operator token">%</SPAN>timeit containsmpl<SPAN class="punctuation token">(</SPAN>a<SPAN class="punctuation token">,</SPAN> ext<SPAN class="punctuation token">)</SPAN>
<SPAN class="number token">24.7</SPAN> ms ± <SPAN class="number token">761</SPAN> µs per loop <SPAN class="punctuation token">(</SPAN>mean ± std<SPAN class="punctuation token">.</SPAN> dev<SPAN class="punctuation token">.</SPAN> of <SPAN class="number token">7</SPAN> runs<SPAN class="punctuation token">,</SPAN> <SPAN class="number token">10</SPAN> loops each<SPAN class="punctuation token">)</SPAN><SPAN class="line-numbers-rows"><SPAN></SPAN><SPAN></SPAN></SPAN>
I think it uses the 'crossing number' method of testing which would be better for general polygons, something which has to be added to the pure numpy approach, after the selection of the points in the extent has been completed.
To come...
(4) pandas in ArcGIS
(5) Other methods to assess 'containment'
Results so far... In all cases, the result was 401 points within the one polygon.
Numpy 0.195 ms (195 microseconds)
MatPlotLib 24.7 ms
SelectByLocation 308 ms
arcpy 472 ms