Author Topic: TMapViewer  (Read 64584 times)

wp

• Hero Member
• Posts: 5451
Re: TMapViewer
« Reply #60 on: January 12, 2019, 11:25:30 pm »
I know these. But I cannot focus at this component at the moment. Patches are welcome.
Lazarus trunk / fpc 3.0.4 / all 32-bit on Win-10

mpknap

• Jr. Member
• Posts: 56
Re: TMapViewer
« Reply #61 on: January 17, 2019, 08:08:50 am »
I have a question. Does anyone know how to calculate X and Y having Lat and Long in this component? Having geographical coordinates I want to draw something on the map at a given point.

Maciej, wiesz może?

wp

• Hero Member
• Posts: 5451
Re: TMapViewer
« Reply #62 on: January 17, 2019, 09:52:50 am »
The MapViewer component has methods LonLatToScreen and reverse ScreenToLonLat:
Code: Pascal  [Select]
1. type
2.   TRealPoint = Record
3.     Lon : Double;
4.     Lat : Double;
5.   end;
6.
7.   TMapView = class(TCustomControl)
8.   [...]
9.   public
10.     function LonLatToScreen(aPt: TRealPoint): TPoint;
11.     function ScreenToLonLat(aPt: TPoint): TRealPoint;
12.     [...]
13.   end;
Lazarus trunk / fpc 3.0.4 / all 32-bit on Win-10

mpknap

• Jr. Member
• Posts: 56
Re: TMapViewer
« Reply #63 on: January 17, 2019, 06:10:08 pm »
Thanks, this component is much more extensive. I studied the "kcmapviewer" component.
I still have a problem. How to draw an ellipse on geographical coordinates, e.g. 40.00E and 15.50N?

Code: Pascal  [Select]
1. var
2.   p: TRealPoint;
3.   a,b : integer;
4. begin
5.   p.Lat:=15.5;
6.   p.Lon:=40;
7.   a:=mapview.LonLatToScreen.:=(p.Lat));
8.   b:=mapview.LonLatToScreen(p.lon);
9.
10.   mapview.Canvas.Ellipse(a,b,a+10,b+10);
11. end;

This code not working :/
« Last Edit: January 17, 2019, 07:44:55 pm by mpknap »

wp

• Hero Member
• Posts: 5451
Re: TMapViewer
« Reply #64 on: January 17, 2019, 08:11:13 pm »
No, this code is not working. In fact, I don't know how to paint into the map directly. It would be rather complicated because the map is not static and changes with zoom level and center of the map.

On the other hand, you probably want to mark some specific locations or a track. For this purpose, the MapViewer (that one by ti_dic which is in a modified version on Lazarus-CCR) has a list for GPS locations. Look at unit mvGpsObj: there is a TGpsPoint which you create with the gps coordinates as argument. Add the point to the GpsItems of the Mapviewer. You must provide some kind of ID to differentiate between the various kind of GPS items. The following code adds a GPS point for the location clicked with the mouse while the CTRL key is pressed:
Code: Pascal  [Select]
1. const
2.   _POINTS_ = 20;
3.
4. procedure TMainForm.MapViewMouseDown(Sender: TObject; Button: TMouseButton;
5.   Shift: TShiftState; X, Y: Integer);
6. var
7.   rPt: TRealPoint;
8.   gpsPt: TGPSPoint;
9. begin
10.   if (Button = mbLeft) and (ssCtrl in Shift) then begin
11.     rPt := MapView.ScreenToLonLat(Point(X, Y));
12.     gpsPt := TGpsPoint.CreateFrom(rPt);
14.   end;
15. end;

The point is drawn as a red cross, but you'll certainly find in the code the procedure where this is defined, and you can replace it by a circle (*).

There is also a TGpsTrack containing a list of TGpsPoints, but I did not study it yet.

To learn more about the component study ti_dic's original code in the "roadbook" application which you can see on https://sourceforge.net/p/roadbook/code/ci/master/tree/. Unfortunately, I was not able to compile the original code, I had to remove the special grid used.

(*) Drawing of the points happens in unit mvMapViewer, procedure TMapView.DrawPt.
« Last Edit: January 17, 2019, 09:19:44 pm by wp »
Lazarus trunk / fpc 3.0.4 / all 32-bit on Win-10

mpknap

• Jr. Member
• Posts: 56
Re: TMapViewer
« Reply #65 on: January 17, 2019, 09:25:39 pm »
It's not exactly about drawing  . This I will deal with later. More about converting from geographic coordinates to x, y. If we can convert X and Y, to Lon and Lat
(in the example attached to the component

Code: Pascal  [Select]
1. procedure TMainForm.MapViewMouseMove (Sender: TObject; Shift: TShiftState;)

maybe we can go the other way.

I have a TXT file with Lat and Long entries. I want to put them on a map in any form (circle, inscription, etc.), so as to indicate that they took place in a given position.

I have the impression that it can be done with the LonLatToScreen function, but I do not understand why it does not work.

Meanwhile, thanks for the tips

wp

• Hero Member
• Posts: 5451
Re: TMapViewer
« Reply #66 on: January 17, 2019, 09:48:03 pm »
The basic steps are in the preceding mail. Since you already know the coordinates, forget about the MouseDown event. You probably have a button which reads the file with the gps coordinates. At the end of the reading process you probably have something like an array "Data[]" of gps coordinates, TRealPoint. You can use the following code to draw the points in the map (not tested). The component automatically takes care of the conversion to screen coordinates. And the marks are persistent, i.e. they follow the map when you zoom or drag it to somewhere else.
Code: Pascal  [Select]
1. uses
2.   mvGpsObj, ...;
3.
4. const
5.   _POINTS_ = 10;
6.
7. var
8.   Data: array of TRealPt;
9.   ...
10.   rPt: TRealpoint;
11.   gpsPt: TGpsPoint;
12. begin
13.   for rPt in Data do begin
14.     gpsPt := TGpsPoint.CreateFrom(rPt);
16.   end;
17. ...

In order to change the color you can add a property ExtraData to the gpsPt. The next code adds every other point in blue:
Code: Pascal  [Select]
1. uses
3. var
5.   i: Integer;
6.   ...
7. begin
8.   for i := Low(Data) to High(Data) do begin
9.     gpsPt := TGpsPoint.createFrom(Data[i]);
10.     if odd(i) then begin
12.       extra.Color := clBlue;
14.     end;
16.   end;

At the moment the MapViewer can only draw crosses. But it should be possible to add a "Shape" parameter (plus, cross, circle, box, triangle) as well as some text to the ExtraData and to use these parameters when drawing the gps point. Alternatively, or better: additionally, the procedure DrawPt should be extended to fire an event OnDrawPoint(AGpsPoint: TGpsPoint) where you can draw the GPSpoint in the way you want.
Lazarus trunk / fpc 3.0.4 / all 32-bit on Win-10

wp

• Hero Member
• Posts: 5451
Re: TMapViewer
« Reply #67 on: January 18, 2019, 12:15:10 am »
I extended the demo in https://sourceforge.net/p/lazarus-ccr/svn/HEAD/tree/components/lazmapviewer/ to show how to add GPS points to the map.
Lazarus trunk / fpc 3.0.4 / all 32-bit on Win-10

mpknap

• Jr. Member
• Posts: 56
Re: TMapViewer
« Reply #68 on: January 18, 2019, 06:53:44 am »
Excellently!!. This is what I ment. Now from the TXT file I will load the items into the list and I can mark points on the map.
Thank you
This list has some limitations?

One more question. I changed the cross into an ellipse. It would be interesting to click on an ellipse (a GPS point) to display eg information in a dialog with properties (some text).
P.S.
I am writing a small program to visualize the detection of cosmic rays on a map. Detections are made by smartphones of people participating in the CREDO project of the Institute of Nuclear Physics in Krakow. I'm just a hobbyist, I do not work there.

GetMem

• Hero Member
• Posts: 3419
Re: TMapViewer
« Reply #69 on: January 18, 2019, 07:28:18 am »
Quote
One more question. I changed the cross into an ellipse. It would be interesting to click on an ellipse (a GPS point) to display eg information in a dialog with properties (some text).
The idea is to create an elliptic region with the bounding rectangle's coordinates, something like this:
Code: Pascal  [Select]
1. uses LCLIntf, LCLType;
2.
3. var
4.   Rgn: HRGN;
5.
6. procedure TForm1.FormCreate(Sender: TObject);
7. begin
8.   Rgn := CreateEllipticRgn(75, 50, 225, 150);
9. end;
10.
11. procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton;
12.   Shift: TShiftState; X, Y: Integer);
13. begin
14.   if PtInRegion(Rgn, X, Y) then
15.     ShowMessage('Inside ellipse')
16.   else
17.     ShowMessage('Outside ellipse');
18. end;
19.
20. procedure TForm1.FormPaint(Sender: TObject);
21. begin
22.   //just for visual feedback, you don't need this in your application
23.   Canvas.Brush.Color := clRed;
24.   Canvas.Ellipse(75, 50, 225, 150);
25. end;

Quote
I am writing a small program to visualize the detection of cosmic rays on a map. Detections are made by smartphones of people participating in the CREDO project of the Institute of Nuclear Physics in Krakow.
Cool.

wp

• Hero Member
• Posts: 5451
Re: TMapViewer
« Reply #70 on: January 18, 2019, 09:42:36 am »
I don't think that this is working because the elliptic region is tied to the Form, but not to the map. So, when the user drags the map to another location or zooms in the region will not follow the gps point.

Scanning through the source code of TMapViewer I found the following possibility. It displays the required information in a label, a pop hint should be possible as well. I added it to the demo on CCR:
Code: Pascal  [Select]
1. procedure TMainForm.MapViewMouseMove(Sender: TObject; Shift: TShiftState;
2.   X, Y: Integer);
3. const
4.   DELTA = 3;
5. var
6.   rArea: TRealArea;
7.   gpsList: TGpsObjList;
8.   L: TStrings;
9.   i: Integer;
10. begin
11.   // Determine area, in GPS coordinates, around the current mouse position +/- DELTA pixels
12.   rArea.TopLeft := MapView.ScreenToLonLat(Point(X-DELTA, Y-DELTA));
13.   rArea.BottomRight := MapView.ScreenToLonLat(Point(X+DELTA, Y+DELTA));
14.   // Retrieve the gps objects in this area
15.   gpsList := MapView.GpsItems.GetObjectsInArea(rArea);
16.   try
17.     if gpsList.Count > 0 then begin
18.       // Create a string from the gps objects found: Name + gps coordinates
19.       L := TStringList.Create;
20.       try
21.         for i:=0 to gpsList.Count-1 do
22.           if gpsList[i] is TGpsPoint then
23.             with TGpsPoint(gpsList[i]) do
24.               L.Add(Format('%s (lat=%.6f°, long=%.6f°)', [Name, Lat, Lon]));
25.         // Assign the created string to a label caption
26.         GPSPointInfo.Caption := L.Text;
27.       finally
28.         L.Free;
29.       end;
30.     end else
31.       GPSPointInfo.Caption := '';
32.   finally
33.     gpsList.Free;
34.   end;
35. end;

Note that the class TGpsPoint, besides Longitude and Latitude, can also hold additional information:
• Name
• DateTime
• Ele: double (elevation?, or whatever that is...)
• ExtraData: TObject, typically TDrawingExtraData containing a Color (for drawing the cross) and some ID (integer)
Lazarus trunk / fpc 3.0.4 / all 32-bit on Win-10

wp

• Hero Member
• Posts: 5451
Re: TMapViewer
« Reply #71 on: January 18, 2019, 06:02:58 pm »
Now I added also an event OnDrawGpsPoint to the MapViewer. It fires for each GPS point added to the GPSItems of the Mapviewer and replaces the default drawing of crosses. The event has as parameters the sending MapViewer, the canvas to draw on, as well as the GPSPoint object. Since the MapViewer in principle supports several drawing backends the canvas is given only as a TObject and must be cast to the "real" canvas used.

The following code, added to the demo project, assumes that the canvas is the FPCustomCanvas set up by default and draws a circle at the GPS point and writes its name above it:
Code: Pascal  [Select]
1. uses
2.   ..., FPCanvas, FPImage, IntfGraphics, ...;
3. procedure TMainForm.MapViewDrawGpsPoint(Sender, ACanvas: TObject;
4.   APoint: TGpsPoint);
5. const
6.   R = 5;
7. var
8.   P: TPoint;
9.   cnv: TFPCustomCanvas;
10.   txt: String;
11.   w, h: Integer;
12.   bmp: TBitmap;
13.   img: TLazIntfImage;
14. begin
15.   // Screen coordinates of the GPS point
16.   P := TMapView(Sender).LonLatToScreen(APoint.RealPoint);
17.
18.   // Draw the GPS point as a circle
19.   cnv := TFPCustomCanvas(ACanvas);
20.   cnv.Brush.FPColor := colRed;
21.   cnv.Ellipse(P.X-R, P.Y-R, P.X+R, P.Y+R);
22.
23.   // Draw the "name" of the GPS point. Note: FPCustomCanvas, by default,
24.   // does not support text output. Therefore we paint to a bitmap first and
25.   // render this on the FPCustomCanvas.
26.   txt := APoint.Name;
27.   bmp := TBitmap.Create;
28.   try
29.     bmp.PixelFormat := pf32Bit;
30.     w := bmp.Canvas.TextWidth(txt);
31.     h := bmp.Canvas.TextHeight(txt);
32.     bmp.SetSize(w, h);
33.     bmp.Canvas.Brush.Color := clWhite;
34.     bmp.Canvas.FillRect(0, 0, w, h);
35.     bmp.Canvas.TextOut(0, 0, txt);
36.     img := bmp.CreateIntfImage;
37.     cnv.Draw(P.X - w div 2, P.Y - h - 2*R, img);
38.     img.Free;
39.   finally
40.     bmp.Free;
41.   end;
42. end;
Lazarus trunk / fpc 3.0.4 / all 32-bit on Win-10

mpknap

• Jr. Member
• Posts: 56
Re: TMapViewer
« Reply #72 on: January 19, 2019, 09:33:50 am »
Thank you very much WP  for your help. I have what I wanted, now I will connect with the database to score points on the map.
I invite everyone interested in the CREDO project to the website :

https://credo.science/

https://github.com/credo-science

https://api.credo.science/web/

avra

• Hero Member
• Posts: 1465
Re: TMapViewer
« Reply #73 on: January 19, 2019, 08:18:16 pm »
Now I added also an event OnDrawGpsPoint to the MapViewer.
This will be very useful. Thanks!
ct2laz - Conversion between Lazarus and CodeTyphon
bithelpers - Bit manipulation for standard types
pasettimino - Siemens S7 PLC lib

WimVan

• Jr. Member
• Posts: 76
Re: TMapViewer
« Reply #74 on: January 23, 2019, 11:29:34 am »
I tried installing LazMapviewer
Then I tried using the demo ..
I got an error which I understand.  I have to pass a proxy to get info from the web.

Where can I tell that proxy is used and where can I enter the proxy-data ?

I see there is a check on the FPC \$IF FPC_FullVersion >= 30101
I using
Lazarus 1.8.4
FPC Version: 3.0.4
SVN 57972

Code: Pascal  [Select]
2. var
3.   http: TFpHttpClient;
4. begin
5.   inherited;
6.   http := TFpHttpClient.Create(nil);
7.   try
8.     http.AllowRedirect := true;
10.    {\$IF FPC_FullVersion >= 30101}
11.     if UseProxy then begin
12.       http.Proxy.Host := ProxyHost;
13.       http.Proxy.Port := ProxyPort;