Coordinates: points within odd-shaped fields

M

Martijn

I've got a terrain that is divided into different zones. Each of these zones
has a different shape. I've got a huge set of XY-coordinates of the corners
of these zones.
Another dataset consists of points (XY-coordinates).

I want to combine these 2 datasets: the points are positioned within the
zones. I want to know the zone-number for each of the points.

Any ideas on how to do this?

An example:
zone - X - Y - corner
1 - 10 - 10 - 1
1 - 20 - 10 - 2
1 - 60 - 20 - 3
1 - 60 - 30 - 4
1 - 10 - 30 - 5
2 - 20 - 10 - 1
2 - 90 - 10 - 2
2 - 90 - 30 - 3
2 - 60 - 30 - 4
2 - 60 - 20 - 5

Sample point: (50,15)


Thank you in advance,
Martijn
 
M

Michel Walsh

You can use a hit test. Here a cut and paste I made from another post which
is related to this concept.
----------------------------------------------

Since this is a mathematical concept, there is no need to have a display
context (hDC) to use the methods (as long as you don't have to render
graphically the region).

The function to use is: PtInRegion( region, x, y)

which tells you if the point (x,y) is, or not, in the region.

What is left to you is to create a 'region'. You do it with CreatePolygonRgn
(there are other way to create regions, but this one is versatile). You send
it an array of points (vertex of the polygon), by giving the start of the
sequence and the number of points to be read. A third argument is used in
case you are grapically inclined to render the region (need an hDC too to do
that).


So, here all the code required to test if one point is, or not, inside a
triangle (in Access, again, since no real graphic rendering is performed):

========================
Option Compare Database
Option Explicit

Private Declare Function PtInRegion Lib "gdi32" (ByVal hRgn As Long, ByVal x
As Long, ByVal y As Long) As Long
Private Declare Function CreatePolygonRgn Lib "gdi32" (lpPoint As Any, ByVal
nCount As Long, ByVal nPolyFillMode As Long) As Long
Private Declare Function DeleteObject Lib "gdi32" (ByVal hObject As Long) As
Long


Private Type POINT
x As Long
y As Long
End Type

Public Sub HitTestSample()
Dim points(1 To 3) As POINT
' define the vertex of a triangle
points(1).x = 0
points(1).y = 0

points(2).x = 100
points(2).y = 0

points(3).x = 0
points(3).y = 100

Dim regionOne As Long

' make a region of or vertexes
regionOne = CreatePolygonRgn(points(1), 3, 2)
' the filling mode has only interest if you use graphical
' display of the region.

If PtInRegion(regionOne, 60, 60) Then Debug.Print "(60,60) in the
triangle"

If PtInRegion(regionOne, 40, 40) Then Debug.Print "(40,40) in the
triangle"

' release memory associated to our variable of type LONG
' since VBA only sees it as a standard LONG variable
DeleteObject regionOne

End Sub
=========================


So, I assume you are likely to end with many regions that you will keep in
an array, or in a collection, and test the point "for each" region in your
collecton (array) of regions.

Final note: since VBA sees only a long, when you speak of a region, the
automatic stack cleaning may not release the memory WIN32 has associated and
made available through this "handle", so it is required to use DeleteObject.




----------------------------


NOTE: Since your regions are defined in a table, your corner number
sequence is important when you define the regions, Indeed, for a square, if
you enter the sequence like:

{ top, left}, {bot, left}, {top, right}, {bot, right}


you don't get a rectangle, but a butterfly as "region" .


As for when using DeleteObject, it is BEFORE loosing the VBA variable of
type long which 'handles' the region (before it goes out of scope, in
general). Be careful with the other case:


Dim x AS region

x= CreatePolygonRegion(.... )
x= CreatePolygonRegion(... )



It is wrong (memory leak) since you lose the first handle when you assign
the variable by something else (here, by assigning a second region to it).

Maybe better to define a CLASS with a TERMINATE event :


Dim x As ClassRegion

x.CreatePolygonRegion( ... )
x.CreatePolygonRegion( ... )



and the class is like:

=========================
Option Compare Database
Option Explicit

Private handle As Long ' your will be handle to a region

Private Sub Class_Terminate()
DeleteObject handle ' delete object automatically called for you
End Sub

Public Sub CreatePolygonRegion( ... )
handle = ...
End Sub

Public Function PointInRegion( ... ) As Boolean
...
End Function
==========================



so the Terminate event being called, AUTOMATICALLY for you, at each
opportune time, such class GREATLY improves the tolerance against memory
leak.

Even more, your class CreatePolygonRegion can accept a single number, and
make the whole job to read the data in the table, etc, which will then ALSO
greatly improve the user-friendly-ness for the developper using such system!





Vanderghast, Access MVP
 
M

Martijn

Thank you for your reply!
I'll just add a loop to test all zones, but this will do the job!
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top