Recent

Author Topic: TMapViewer  (Read 69865 times)

wp

  • Hero Member
  • *****
  • Posts: 5723
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: 72
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: 5723
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: 72
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: 5723
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);
  13.     MapView.GPSItems.Add(gpsPt, _POINTS_);
  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: 72
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: 5723
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);
  15.     MapViewer.GpsObj.Add(gpsPt, _POINTS_);
  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
  2.   mvExtraData, vmGpsObj, ...;
  3. var
  4.   extra: TDrawingExtraData;
  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
  11.       extra := TDrawingExtraData.Create;
  12.       extra.Color := clBlue;
  13.       gpsPt.ExtraData := extra;
  14.     end;
  15.     Mapviewer.GpsObj.Add(gpsPt, _POINTS_);
  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: 5723
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: 72
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: 3459
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: 5723
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: 5723
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: 72
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/

https://www.facebook.com/credo.science/


avra

  • Hero Member
  • *****
  • Posts: 1531
    • Additional info
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]
  1. procedure TMVDEFPC.DownloadFile(const Url: string; AStream: TStream);
  2. var
  3.   http: TFpHttpClient;
  4. begin
  5.   inherited;
  6.   http := TFpHttpClient.Create(nil);
  7.   try
  8.     http.AllowRedirect := true;
  9.     http.AddHeader('User-Agent','Mozilla/5.0 (compatible; fpweb)');
  10.    {$IF FPC_FullVersion >= 30101}
  11.     if UseProxy then begin
  12.       http.Proxy.Host := ProxyHost;
  13.       http.Proxy.Port := ProxyPort;
  14.       http.Proxy.UserName := ProxyUserName;
  15.       http.Proxy.Password := ProxyPassword;
  16.     end;
  17.    {$ENDIF}
  18.     http.Get(Url, AStream);
  19.     AStream.Position := 0;
  20.   finally
  21.     http.Free;
  22.   end;
  23. end;
  24.