Determine if map coordinates are within area

EvonS

Board Regular
Joined
Jun 1, 2016
Messages
111
Office Version
  1. 365
Platform
  1. Windows
  2. Web
I have this information below that define areas on a map. Is there a way to determine if a longitude and latitude are within one of these areas?

OBJECTIDAGENCYDIVUNIT_NAMEADDRESSCITYAREA_SQKMShape__AreaShape__Length
1​
Toronto Police ServiceD1111 Division2054 Davenport RdToronto
18.67845803​
35692935.07​
28190.11106​
2​
Toronto Police ServiceD1212 Division200 Trethewey DrToronto
24.78034332​
47417573.68​
33029.13457​
3​
Toronto Police ServiceD1313 Division1435 Eglinton Av WToronto
18.60191063​
35591722.93​
24616.57917​
4​
Toronto Police ServiceD1414 Division350 Dovercourt RdToronto
14.17525415​
27083979.14​
30017.70564​
5​
Toronto Police ServiceD2222 Division3699 Bloor St WToronto
68.12188386​
130097609.1​
72765.03189​
6​
Toronto Police ServiceD2323 Division5230 Finch Av WToronto
57.64228008​
110369052.3​
54270.05361​
7​
Toronto Police ServiceD3131 Division40 Norfinch DrToronto
42.66749447​
81789149.85​
44477.14656​
8​
Toronto Police ServiceD3232 Division30 Ellerslie AvToronto
61.06048027​
117079222.9​
43658.49725​
9​
Toronto Police ServiceD3333 Division50 Upjohn RdToronto
52.7888815​
101244281.9​
45148.84874​
10​
Toronto Police ServiceD4141 Division2222 Eglinton Av EToronto
44.57511239​
85395988.8​
58875.05415​
11​
Toronto Police ServiceD4242 Division242 Milner Av EToronto
84.61174885​
162528804.9​
59824.09155​
12​
Toronto Police ServiceD4343 Division4331 Lawrence Av EToronto
59.37000474​
113873893.7​
54424.50711​
13​
Toronto Police ServiceD5151 Division51 Parliament StToronto
8.601682841​
16437802.1​
25324.5062​
14​
Toronto Police ServiceD5252 Division255 Dundas St WToronto
9.228055772​
17625448.21​
35966.54447​
15​
Toronto Police ServiceD5353 Division75 Eglinton Av WToronto
31.29485302​
59895251.81​
37395.46015​
16​
Toronto Police ServiceD5555 Division101 Coxwell AvToronto
41.03398633​
78486159.8​
82307.80296​
 
@KRice and @lrobbo314 , thank you so much. I've incorporated your work into my submission form and it works perfectly. And thank you to everyone else who took the time to reply. You all are awesome.
 
Upvote 0

Excel Facts

Square and cube roots
The =SQRT(25) is a square root. For a cube root, use =125^(1/3). For a fourth root, use =625^(1/4).
@KRice,

Nice workbook you uploaded. I found the same geojson files and played around with the PowerQuery portion of it.

I managed to get it down to just the 2 queries in PQ.

Here is what I came up with.

It appends a letter to Divisions that had multiple polygons.

This was a really fun project and learned some cool new things along the way.

tbl_Main
Power Query:
let
    Source = Json.Document(File.Contents("C:\Users\UserName\Downloads\TPS_Police_Divisions.geojson")),
    Custom1 = Source[features],
    ToTable = Table.FromList(Custom1, Splitter.SplitByNothing(), null, null, ExtraValues.Error),
    Main = Table.ExpandRecordColumn(ToTable, "Column1", {"type", "properties", "geometry"}, {"type", "properties", "geometry"}),
    Properties = Table.ExpandRecordColumn(Main, "properties", {"OBJECTID", "AGENCY", "DIV", "UNIT_NAME", "ADDRESS", "CITY", "AREA_SQKM", "Shape__Area", "Shape__Length"}, {"OBJECTID", "AGENCY", "DIV", "UNIT_NAME", "ADDRESS", "CITY", "AREA_SQKM", "Shape__Area", "Shape__Length"}),
    Geometry = Table.ExpandRecordColumn(Properties, "geometry", {"type", "coordinates"}, {"type.1", "coordinates"}),
    Coordinates = Table.ExpandListColumn(Geometry, "coordinates"),
    Index = Table.AddIndexColumn(Coordinates, "Index", 1, 1, Int64.Type),
    NewDiv = Table.AddColumn(Index, "DIVX", (ro)=> if List.Count(List.Select(Index[DIV], each _ =  ro[DIV])) = 1 then ro[DIV] else ro[DIV] & "-" & Character.FromNumber(List.Count(List.Select(List.FirstN(Index[DIV],ro[Index]), each _ = ro[DIV]))+64)),
    RC = Table.RemoveColumns(NewDiv,{"type", "OBJECTID", "DIV", "coordinates", "Index", "type.1"}),
    Reorder = Table.ReorderColumns(RC,{"AGENCY", "DIVX", "UNIT_NAME", "ADDRESS", "CITY", "AREA_SQKM", "Shape__Area", "Shape__Length"}),
    Types = Table.TransformColumnTypes(Reorder,{{"Shape__Length", type number}, {"Shape__Area", type number}, {"AREA_SQKM", type number}, {"CITY", type text}, {"ADDRESS", type text}, {"UNIT_NAME", type text}, {"DIVX", type text}, {"AGENCY", type text}})
in
    Types

tbl_Coordinates
Power Query:
let
    Source = Json.Document(File.Contents("C:\Users\UserName\Downloads\TPS_Police_Divisions.geojson")),
    Custom1 = Source[features],
    ToTable = Table.FromList(Custom1, Splitter.SplitByNothing(), null, null, ExtraValues.Error),
    Main = Table.ExpandRecordColumn(ToTable, "Column1", {"type", "properties", "geometry"}, {"type", "properties", "geometry"}),
    Properties = Table.ExpandRecordColumn(Main, "properties", {"OBJECTID", "AGENCY", "DIV", "UNIT_NAME", "ADDRESS", "CITY", "AREA_SQKM", "Shape__Area", "Shape__Length"}, {"OBJECTID", "AGENCY", "DIV", "UNIT_NAME", "ADDRESS", "CITY", "AREA_SQKM", "Shape__Area", "Shape__Length"}),
    Geometry = Table.ExpandRecordColumn(Properties, "geometry", {"type", "coordinates"}, {"type.1", "coordinates"}),
    Coordinates = Table.ExpandListColumn(Geometry, "coordinates"),
    Index = Table.AddIndexColumn(Coordinates, "Index", 1, 1, Int64.Type),
    NewDiv = Table.AddColumn(Index, "DIVX", (ro)=> if List.Count(List.Select(Index[DIV], each _ =  ro[DIV])) = 1 then ro[DIV] else ro[DIV] & "-" & Character.FromNumber(List.Count(List.Select(List.FirstN(Index[DIV],ro[Index]), each _ = ro[DIV]))+64)),
    ExpandMult = Table.TransformColumns(NewDiv,{{"coordinates", each if List.Count(_) = 1 then _{0} else _}}),
    RC = Table.RemoveColumns(ExpandMult,{"type", "AGENCY", "ADDRESS", "CITY", "AREA_SQKM", "Shape__Area", "Shape__Length", "type.1", "OBJECTID", "Index", "DIV"}),
    Reorder = Table.ReorderColumns(RC,{"DIVX", "UNIT_NAME", "coordinates"}),
    ExpandedCoords = Table.ExpandListColumn(Reorder, "coordinates"),
    ExtractValues = Table.TransformColumns(ExpandedCoords, {"coordinates", each Text.Combine(List.Transform(_, Text.From), ";"), type text}),
    Split = Table.SplitColumn(ExtractValues, "coordinates", Splitter.SplitTextByDelimiter(";", QuoteStyle.Csv), {"Longitude", "Latitude"}),
    Types = Table.TransformColumnTypes(Split,{{"Latitude", type number}, {"Longitude", type number}, {"UNIT_NAME", type text}, {"DIVX", type text}})
in
    Types

CAN InPoly II.xlsm
ABCDEFGHIJ
1Bounding Boxes
2Street AddressCityDivisionLongMinLongMaxLatMinLatMaxIn Polygon
3User Inputs45 Esandar DrTorontoD11-79.5122-79.428643.6317143.67511Not In Polygon
4Lat-Long of address43.706916-79.356874D12-79.5396-79.45643.6626443.72469Not In Polygon
5Location in:D53D13-79.4698-79.407343.6672743.71927Not In Polygon
6Send to:75 Eglinton Av WTorontoD14-79.4462-79.391643.6262743.67544Not In Polygon
7D22-79.6087-79.469143.58143.68413Not In Polygon
8D23-A-79.6393-79.510243.6646343.76303Not In Polygon
9D23-B-79.5654-79.565443.7373643.73743Not In Polygon
10D31-79.5852-79.472543.7128243.7839Not In Polygon
11D32-79.4845-79.381143.7108143.80327Not In Polygon
12D33-79.3963-79.302643.7162443.81566Not In Polygon
13D41-79.3198-79.219143.6710743.77934Not In Polygon
14D42-79.3413-79.133943.7683743.85546Not In Polygon
15D43-79.2648-79.115543.7082943.80463Not In Polygon
16D51-79.3868-79.337743.6332143.67533Not In Polygon
17D52-A-79.4049-79.349443.6119643.63535Not In Polygon
18D52-B-79.4039-79.374243.6370243.67023Not In Polygon
19D53-79.4298-79.335443.6666743.73037In Polygon
20D55-79.3664-79.2843.6127443.7263Not In Polygon
User_Input
Cell Formulas
RangeFormula
E3:E20E3=UNIQUE(Table_tbl_Main[DIVX])
F3:F20F3=MINIFS(Table_tbl_Coordinates[Longitude],Table_tbl_Coordinates[DIVX],User_Input!E3#)
G3:G20G3=MAXIFS(Table_tbl_Coordinates[Longitude],Table_tbl_Coordinates[DIVX],User_Input!E3#)
H3:H20H3=MINIFS(Table_tbl_Coordinates[Latitude],Table_tbl_Coordinates[DIVX],User_Input!E3#)
I3:I20I3=MAXIFS(Table_tbl_Coordinates[Latitude],Table_tbl_Coordinates[DIVX],User_Input!E3#)
B4:C4B4=TEXTSPLIT(TEXTBEFORE(TEXTAFTER(WEBSERVICE("http://geocoder.ca/?locate=" & SUBSTITUTE(B3," ","+")&"%2c+"&C3&"%2c+Ontario&geoit=GeoCode+it!"),"""ICBM"" content="""),""""),", ")+0
B5B5=INDEX(E3#,MATCH("In Polygon",$J$3:$J$20,0))
B6:C6B6=FILTER(Table_tbl_Main[[ADDRESS]:[CITY]],Table_tbl_Main[DIVX]=User_Input!B5)
J3:J20J3=IF(PtInPoly($C$4,$B$4,FILTER(Table_tbl_Coordinates[[Longitude]:[Latitude]],Table_tbl_Coordinates[DIVX]=User_Input!E3)),"","Not ")&"In Polygon"
Dynamic array formulas.


And yes, Rick's PtInPoly algorithm works perfectly.
 
Upvote 0
Also,

Here is a conversion of Rick's PtInPoly algorithm to a LAMBDA function.

Excel Formula:
=LAMBDA(XC,YC,P,
    IF(MOD(SUM(
        SCAN(0,SEQUENCE(ROWS(P)-1),
            LAMBDA(s,c,
                IF(XOR(INDEX(P,c,1)>XC,INDEX(P,c+1,1)>XC),
                    LET(
                        m,(INDEX(P,c+1,2)-INDEX(P,c,2))/(INDEX(P,c+1,1)-INDEX(P,c,1)),
                        b,(INDEX(P,c,2)*INDEX(P,c+1,1)-INDEX(P,c,1)*INDEX(P,c+1,2))/(INDEX(P,c+1,1)-INDEX(P,c,1)),
                        IF(m*XC+b>YC,s+1,s+0)
                    ),0
                )
            )
        )
    ),2),"In Polygon","Not In Polygon"
    )
)
 
Upvote 0
Just curious @Rick Rothstein ... Did you come up with that and know how to do the math off the top of your head? Or did you look up pseudo-code and translate it to VBA?
 
Upvote 0
Just curious @Rick Rothstein ... Did you come up with that and know how to do the math off the top of your head? Or did you look up pseudo-code and translate it to VBA?
I developed the original code for that function many years ago (maybe 15 to 20 years ago now) when I was volunteering in the Visual Basic (the original compiled version, not the one Microsoft calls Visual Basic now) newsgroups (the predecessor to forum for you young'un out there). I developed the math myself... I was a math major in college and a licensed Professional Civil Engineer (designing roads and such) for the bulk of my working career, so developing the function was not that difficult for me. If you look through the entire thread at the link I provided, you will find some modification on the code in response to requests that were made to me.
 
Last edited:
Upvote 0
Wow. That is very impressive. I tried to figure out what was going on there, and it's a bit beyond me. But I'm inspired to go dig deeper into it.

Thanks for the update.
 
Upvote 0
@lrobbo314, I came across this reference when I was investigating various "point in polygon" strategies...before Rick pointed toward a very nice solution that could be incorporated into the OP's form.
At one point, that page offered the entire book, but the book proper appears to have been removed and is now available through other outlets (I have a pdf version of it). The book contains a couple of strategies for point-in-polygon and some good references, as well as strategies for solving many other types of geometry problems. While the book contains a lot of pseudo-code, the C++ code for the algorithms is still available for free download through the website. One of the strategies describes counting edge crossings.
@Rick Rothstein, you mentioned 15-20 years ago. After exploring the link you provided, I found a download for a file you created in 2003 that included the point-in-polygon UDF. It's remarkable that a generalized algorithm developed long ago has worked so well for solving a variety of different problems.
 
Last edited:
Upvote 0
What was your search criteria for finding that 2003 article? The earliest I could find was one written 2013. What really amazed me across the years about the Point In Polygon function was the variety of areas people wrote that they found a use for it in... an eye doctor wrote that he used it for some kind of vision mapping need, someone used it to determine the spacing of streetlights within a section of their town, another used it for some kind of soil mapping application, someone even used it for determining the winner of a hot air balloon contest, and the list goes on.
 
Upvote 0

Forum statistics

Threads
1,216,505
Messages
6,131,026
Members
449,616
Latest member
PsychoCube

We've detected that you are using an adblocker.

We have a great community of people providing Excel help here, but the hosting costs are enormous. You can help keep this site running by allowing ads on MrExcel.com.
Allow Ads at MrExcel

Which adblocker are you using?

Disable AdBlock

Follow these easy steps to disable AdBlock

1)Click on the icon in the browser’s toolbar.
2)Click on the icon in the browser’s toolbar.
2)Click on the "Pause on this site" option.
Go back

Disable AdBlock Plus

Follow these easy steps to disable AdBlock Plus

1)Click on the icon in the browser’s toolbar.
2)Click on the toggle to disable it for "mrexcel.com".
Go back

Disable uBlock Origin

Follow these easy steps to disable uBlock Origin

1)Click on the icon in the browser’s toolbar.
2)Click on the "Power" button.
3)Click on the "Refresh" button.
Go back

Disable uBlock

Follow these easy steps to disable uBlock

1)Click on the icon in the browser’s toolbar.
2)Click on the "Power" button.
3)Click on the "Refresh" button.
Go back
Back
Top