Scale point to a frame

mebcs

Member
Licensed User
Longtime User
It has been too long since I've scaled values. Anyone want to help me?

B4X:
I have a frame area :

y1 +------------------------------------------------------------------+
   |                                                                  |
   |                                                                  |
   |                                                                  |
   |                                                                  |
   |                                                                  |
   |                                                                  |
   |                                                                  |
   |                                                                  |
   |                                                                  |
   |                                                                  |
   |                                                                  |
   |                                                                  |
   |                                                                  |
   |                                                                  |
   |                                                                  |
   |                                                                  |
   |                                                                  |
   |                                                                  |
y2 +------------------------------------------------------------------+
   x1                                                                 x2

X1 = 40
x2 = 525
widthX = x2 - x1 = 485

y1 = 50
y2 = 220
heightY = y2 - y1 = 170

I need to scale points to the frame:

pointsMinX = 3000
pointsMaxX = 4200

pointsMinY = 5000
pointsMaxY = 6400

Example point to scale to frame:

PointX = 3200
PointY = 5450
 

klaus

Expert
Licensed User
Longtime User
Here you are :
B4X:
Sub CalcScale
    ScaleX = FrameWidth / PointsWidth
    ScaleY = FrameHeight / PointsHeight
End Sub

Sub PointToFrameX(px As Int) As Int
    Return (px - PointsMinX) * ScaleX + FrameX1
End Sub

Sub PointToFrameY(py As Int) As Int
    Return (py - PointsMinY) * ScaleY + FrameY1
End Sub

Sub FrameToPointX(x As Int) As Int
    Return (x - FrameX1) / ScaleX + PointsMinX
End Sub

Sub FrameToPointY(y As Int) As Int
    Return (y - FrameY1) / ScaleY + PointsMinY
End Sub
Attached a small test program.

Best regards.
 

Attachments

  • ScaleFrame.zip
    6.2 KB · Views: 189
Upvote 0

mebcs

Member
Licensed User
Longtime User
Thanks Klaus,

The code makes perfect sense. I just couldn't get my head straight.:sign0060:
 
Upvote 0

mebcs

Member
Licensed User
Longtime User
FYI - Able to show translated ESRI Shape File now

klaus,
I wanted to show the results of using the modified code you posted.

I know I can use FrameToPointY,FrameToPointX with Activity_Touch but can I relate it to the nearest X,Y points of the a line created with DrawLine?
I need to get a tag for polygon. Each line drawn has a tag stored.
 

Attachments

  • afdi_info_001.jpg
    afdi_info_001.jpg
    43.4 KB · Views: 208
Last edited:
Upvote 0

klaus

Expert
Licensed User
Longtime User
When you draw a line with a Canvas, only pixels are drawn onto the screen but the system doesn't know anything about the line.
If you want to acces the line in any way you must memorize its coordinates with an index. To find if the current point on the sceen is near a line you need to calculate the distance between the point and all lines.
Are you writing a CAD program ?

Best regards.
 
Upvote 0

mebcs

Member
Licensed User
Longtime User
I am creating a display of an ESRI Shape file I converted.
It is a polygon file. When I click on the display I want to know the polygon used to create it. I have all the points in a database already so it would be easy to search. I just don't how and what to search for.
The table is described and created as:

B4X:
   m.Initialize
   m.Put(StringDefs.MapNames_MapID, DBUtils.DB_TEXT) 
   m.Put(StringDefs.MapNames_MapSource, DBUtils.DB_TEXT)
   m.Put(StringDefs.MapNames_MinX, DBUtils.DB_REAL)
   m.Put(StringDefs.MapNames_MaxX, DBUtils.DB_REAL)
   m.Put(StringDefs.MapNames_CenX, DBUtils.DB_REAL)
   m.Put(StringDefs.MapNames_MinY, DBUtils.DB_REAL)
   m.Put(StringDefs.MapNames_MaxY, DBUtils.DB_REAL)
   m.Put(StringDefs.MapNames_CenY, DBUtils.DB_REAL)
   DBUtils.CreateTable(database, "MapNames", m,  "") 

   m.Initialize
   m.Put(StringDefs.MapShapes_MapID, DBUtils.DB_TEXT) 
   m.Put(StringDefs.MapShapes_ShapeID, DBUtils.DB_TEXT)
   m.Put(StringDefs.MapShapes_PID, DBUtils.DB_TEXT)
   DBUtils.CreateTable(database, "MapShapes", m,  "") 

   m.Initialize
   m.Put(StringDefs.MapPoints_ShapeID, DBUtils.DB_TEXT) 
   m.Put(StringDefs.MapPoints_PointID, DBUtils.DB_TEXT)
   m.Put(StringDefs.MapPoints_X, DBUtils.DB_REAL)
   m.Put(StringDefs.MapPoints_Y, DBUtils.DB_REAL)
   DBUtils.CreateTable(database, "MapPoints", m,  "")

MapNames contains a row for shapefile, I currently only use 1
MapShapes contains a row for each polygon, I use StringDefs.MapShapes_PID as a tag to relate it to other data.
MapPoints contains a row for each segment of the polygon

StringDefs is a global code module containing string "constants"

I extrach the points from the database to a List and then draw them:

B4X:
Private Sub DrawMap
   Dim x1, x2, y1, y2       As Float
   Dim Shape1             As Int
   Dim Shape2             As Int
   Dim pin             As Int
   Dim point1             As MapPoint
   Dim point2             As MapPoint
   Dim PointsToProcess    As Int
   If md.Points.Size >0 Then
      point1 = md.Points.Get(0)
      x1 = point1.X 
      y1 = point1.Y
      Shape1 = point1.Shape 
      
      PointsToProcess = md.Points.Size - 1
      
      For i = 1 To PointsToProcess
         point2 = md.Points.Get(i)
         x2 = point2.X 
         y2 = point2.Y
         pin = point2.point 
         Shape2 = point2.Shape 
         If Shape1 = Shape2 Then
            DrawLine(x1,y1,x2,y2,1dip)
         End If
         x1 = x2
         y1 = y2
         Shape1 = Shape2
      Next
   Else
      ToastMessageShow("No map elements.", True)
   End If
End Sub
 
Last edited:
Upvote 0

klaus

Expert
Licensed User
Longtime User
You'll need a loop like your drawing loop but you must calculate the distance between the current point and each line.
If the distance is amaller than a given value and the point is inside the line bounds then the line is close to the point.

Routine to calculate the distance between a point and a line.
B4X:
'Returns the distance between a point p(x, y) and line defined by the two points p(xa, ya) and p(xb, yb)
Sub CalcDistanceLinePoint(xa As Double, ya As Double, xb As Double, yb As Double, x As Double, y As Double)
    Dim a, b, c, d As Double
    
    a = yb - ya
    b = xa - xb
    c = -a * xa - b * ya
    d = (a * x + b * y + c) / Sqrt(a * a + b * b)    
    Return d
End Sub
In this routine if the distance is negative then the point is on the right side of the line when looking from point a to point b.

Attached a small test program showing the principle.

With many lines the routine could need quite some time.
With close lines it could be difficult to select one with a finger.

Best regards.
 

Attachments

  • DistancePointLine.zip
    6.6 KB · Views: 184
Upvote 0

mebcs

Member
Licensed User
Longtime User
klaus, works great, see attached image.
Is there any way to zoom in?
I think i can do it by selecting all points within a rectangle and redrawing the map, but it takes a long time.
 

Attachments

  • afdi_info_002.jpg
    afdi_info_002.jpg
    37.8 KB · Views: 193
Upvote 0

klaus

Expert
Licensed User
Longtime User
I'm afraid that this is the only way.
You can do it either with bottons and fixed zoom ratios or as you already mentioned with a rectangle.
With the rectangle take care of the width/height ratio.
You could also draw onto a Panel bigger than the screen and move it.
But, in that case, be careful not to have a too big panel because of memory lack.

Best regards.
 
Upvote 0

klaus

Expert
Licensed User
Longtime User
Hi Mark,
I had a look at your app, looks pretty nice.
Just one question, when you zoom by defining a rectangle, are you sure that the scaling is correct ?
To me it seems that the zoomed image is streched horizontally.
I would suggest you to calculate the center coordinates of the user defined rectangle, center the zoom rectangle to these coordinates and use either the width ot the height of the user defined rectangle according to the width/height ratio of the display area.

Best regards.
 
Upvote 0
Top