LazMapViewer - Lines between realpoints lazarus / 39-Graphics ---------------------------------------------------------------------- Arctic_Eddie · 2022-08-07 17:51 I've been using the viewer package from the OPM for a ham radio propagation project. The purpose is to display the path from a sending station (TX) using WSPR (Weak Signal Propagation Report) and all those hearing the signal (RX) and reporting the spot to the WSPR website. There is one problem trying to draw lines from the TX station to all RX stations. The only thing that has worked so far is the below code. Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i procedure TForm1.DisplayTrack( trpt1, trpt2 :TRealPoint );var     rxpt, txpt       :TPoint;begin     // Display a line between the TX and Rx stations     WSPRMap.DrawingEngine.PenWidth:=1;     WSPRMap.DrawingEngine.PenColor:=clRed;     txpt := WSPRMap.LonLatToScreen( trpt1 );      // TX screen point     rxpt := WSPRMap.LonLatToScreen( trpt2 );      // RX screen point     WSPRMap.DrawingEngine.Line(txpt.x,txpt.y,rxpt.x,rxpt.y);end;  The problem is that when the map is zoomed or panned then the drawing engine deletes the tracks and they have to be displayed again. So my question is: what method should be used to draw these lines whereby the engine is aware of the change and redraws them? ---------------------------------------------------------------------- wp · 2022-08-07 21:04 Don't paint outside the paint cycle - that's a general rule, not related to the MapViewer. Because each control must be able to repaint itself at any time if the operating system requires that. The "official" way to add a line to the mapview is to add a TGpsTrack to the GpsItems list of the MapViewer. The MapViewer "knows" what a track is and how to draw it because a track consists of discrete points connected by straight lines. If the endpoints of your line are close to each other it is sufficient to simply add the endpoints to the track. When the line covers a large distance, however, you must divide the line into several segements and add these to the track as well; this way it is guaranteed that the line is curved due to the spherical projection of the grand circle between start and end point. In the attachment you find a simple example for adding such a segmented "line" to the mapviewer. ---------------------------------------------------------------------- Arctic_Eddie · 2022-08-07 21:27 Thank you so much. I'll add that to my project tomorrow. I have other questions but will put them in a separate post. ---------------------------------------------------------------------- Arctic_Eddie · 2022-08-08 12:01 Unfortunately, the compile failed in the same way as your ADrawer example. The error list is shown below. I'm missing something common in both situations. Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i Compile Project, Target: bin\x86_64-win64\mapviewer_line_demo.exe: Exit code 1, Errors: 4, Hints: 2Hint: Start of reading config file C:\lazarus\fpc\3.2.0\bin\x86_64-win64\fpc.cfgHint: End of reading config file C:\lazarus\fpc\3.2.0\bin\x86_64-win64\fpc.cfgmain.pas(55,12) Error: identifier idents no member "LineColor"main.pas(56,12) Error: identifier idents no member "LineWidth"main.pas(63,46) Error: Identifier not found "TRealPoint"main.pas(82,23) Error: Identifier not found "RealPoint" ---------------------------------------------------------------------- wp · 2022-08-08 15:57 Ah, I see - you seem to be using the OPM version which is not in sync with the development version. Please install the version from ccr; either by using svn, or get the snapshot from https://sourceforge.net/p/lazarus- ccr/svn/HEAD/tree/components/lazmapviewer/ ---------------------------------------------------------------------- Arctic_Eddie · 2022-08-08 17:03 What do I need to remove of the OPM version? ---------------------------------------------------------------------- wp · 2022-08-08 17:18 Not absolutely necessary since the IDE always used the version that was installed last. But if you want to do it: Check LazMapViewer in OPM, then click "Uninstall". In order to install the new ccr version unzip the snapshot to some folder, then, in the IDE, go to "Package" > "Open package file (.lpk)" > navigate to the folder with the ccr version > open "lazmapviewerpkg.lpk" > "Use" > "Install". If you use the add-on packages in your project you must also install the other lpk files in the same way. ---------------------------------------------------------------------- Arctic_Eddie · 2022-08-08 17:28 It was the other three lpk files that concerned me. I do not use them knowingly but will find out. I'll uninstall the old LazMapViewer and install the new one as you suggested. ---------------------------------------------------------------------- Arctic_Eddie · 2022-08-08 18:39 The BGA object was causing a problem so I uninstalled it and now your line demo works. However, when trying to use your example below, I get several failures. Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i procedure TForm1.MapView1DrawGpsPoint(Sender: TObject;  ADrawer: TMvCustomDrawingEngine; APoint: TGpsPoint);const  R = 8;var  p: TPoint;begin  // Screen coordinates of the GPS point  p := MapView1.LonLatToScreen(APoint.RealPoint);   // Draw a blue circle  ADrawer.PenColor := clBlack;  ADrawer.BrushColor := clBlue;  ADrawer.BrushStyle := bsSolid;  ADrawer.Ellipse(p.X-R, p.Y-R, P.X+R, p.Y+R);   // Draw the point label  ADrawer.BrushStyle := bsClear;  ADrawer.FontName := 'SegoeUI';  ADrawer.FontSize := 16;  ADrawer.FontStyle := [fsBold];  ADrawer.TextOut(    p.X - ADrawer.TextWidth(APoint.Name) div 2,    p.Y + R + 4,    APoint.Name  );end;The ellipse function is unknown and causes a SIGSEGV fault. If I skip over that the blue circle does not draw anything as the TextWidth function returns a zero. If I substituted a real value, 20, it still does not draw. As soon as I click anything in my program, I get another SIGSEGV fault. Is this example not compatible with the dev version? ---------------------------------------------------------------------- wp · 2022-08-08 21:50 I am aware of crashes on Linux due to the GPS points and tracks painting, but not on Windows (which you seem to have). That the drawer's Ellipse method is unknown could indicate that your new version is not correctly installed. Try this: Open "Tools" > "Configure Build Lazarus" and check the boxes "Clean all" and "Switch after building to automatically". Then click "Build". Or: Open "Package" > "Install/Uninstall packages", and select the "lazmapviewerpkg.lpk" in the left listbox. When you scroll down the "Package Info" box you should see the path from where the package is used. Is this the path into which you unzipped the download? If not repeat the installation as described in the previous message. ---------------------------------------------------------------------- Arctic_Eddie · 2022-08-08 22:30 I'm using an Acer desktop running Win10/21H2, 12GB of RAM and a 512GB SSD drive. I did both of your suggestions and it used my downloaded snapshot from the link you gave. However, both faults are still present in that example but everything else run fine. I also discovered that LineWidth appears to be in millimeters. I noticed the type was Double so tried 0.5 and settled on 0.3. It also affected the size of the lines in the + mark. Would like to be able to use multiple colors for the tracks as a hundred spot reports all in red is a little overwhelming. One method that comes to mind is to have maybe 16 versions of _LINE_ and sprinkle the spots randomly into these groups with each having a different color. Would that be feasible? PS ADrawer.TextWidth(APoint.Name); does not work either. It always returns zero. I tried 20 but it did not help. Is ADrawer.BrushStyle := bsClear; feasible. Maybe I can't see it because it's invisible. ---------------------------------------------------------------------- wp · 2022-08-08 23:21 The attached modified demo of the previous post contains your OnDrawGpsPoint handler (right-click somewhere to add a gps point) - it is working correctly for me (Win 11, 64bit, Laz main/fpc 3.2.2/64bit and 32 bit). Setting a breakpoint after the line w := ADrawer.TextWidth(APoint.Name) allows to read a non-zero TextWidth. I am rather clueless now... [EDIT] Adding the forgotten project... ---------------------------------------------------------------------- Arctic_Eddie · 2022-08-08 23:38 I don't see the attachment. After attempting to run the earlier example, and fail, I get a crash even closing my form. PS I'm also using Laz 2.0.12. Perhaps that's the problem. ---------------------------------------------------------------------- Arctic_Eddie · 2022-08-09 17:07 I set a breakpoint at the Ellipse procedure then "Step-Into" to see where it was going. Strangely, it goes to intfbasewinapi.inc to this point. Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i function TWidgetSet.CreateFontIndirectEx(const LogFont: TLogFont;  const LongFontName: string): HFONT;begin  // this functions is needed, because the fontname in TLogFont is limited to  // 32 characters. If the interface does not support long font names, it can  // simple omit this function  Result := CreateFontIndirect(LogFont);end; Nothing makes any sense at that point and I wonder what fonts have to do with drawing an ellipse. ---------------------------------------------------------------------- wp · 2022-08-09 17:21 Quote from: Arctic_Eddie on August 09, 2022, 01:38:49 amI don't see the attachment. Sorry...  Added it to the previous post now. Quote from: Arctic_Eddie on August 09, 2022, 01:38:49 amI'm also using Laz 2.0.12. Perhaps that's the problem. No. Installed the ccr-mapviewer version in Laz 2.0.12., and that mapviewer-line sample project is working correctly here, too (well... there is another issue because when the gps point title is painted over the map tile boundary it is overpainted by the neighbouring tile - but this is a different story.) ---------------------------------------------------------------------- Arctic_Eddie · 2022-08-09 17:24 I tried the same thing with the call to ADrawer.TextOut() and ended up in win32winapi.inc  at this function. Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i function TWin32WidgetSet.CreateRoundRectRgn(X1, Y1, X2, Y2, nWidthEllipse,  nHeightEllipse: Integer): HRGN;begin  Result := Windows.CreateRoundRectRgn(X1, Y1, X2, Y2, nWidthEllipse,    nHeightEllipse);end; This one is talking about drawing an ellipse when it should be dealing with text. The X1 and Y1 values are reasonable but the next two are huge. It looks like each procedure is aimed at what the other should be doing. This is getting a lot deeper than I want to go just to print ham radio call signs on a world map. ---------------------------------------------------------------------- Arctic_Eddie · 2022-08-09 17:44 Your second demo runs fine. I don't see any difference between what you have and mine. There must be a system setting in my PC that is causing the problem. I compared my Form.Activate with yours and nothing obvious. Would you like me to post my Form.Activate and Form.Create procedures? Maybe you can spot something there. ---------------------------------------------------------------------- wp · 2022-08-09 17:52 Yes post them, but reading the sources usually is not very efficient without support by the compiler. If the project is not too big it would be better to provide the full project, i.e. all pas, lfm, the lpi and lpr files so that I can compile and test myself. Of course you should remove everything not needed from the project ---------------------------------------------------------------------- Arctic_Eddie · 2022-08-09 18:04 The project is about 1700 lines. Here's the two procedures. Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i procedure TForm1.FormActivate(Sender: TObject);var   P: TRealPoint;begin     // Set up initial map      P.Lon := 0;                 // Just north of the equator     P.Lat := 10;     WSPRMap.Center := P;     WSPRMap.Zoom := DEF_ZOOM;   // Nice starting view     WSPRMap.DoubleBuffered := true;     WSPRMap.Active := true;end;  Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i procedure TForm1.FormCreate(Sender: TObject);var     sw, sh, ft, fl  :Integer;begin     // 1366x768 but account for title bar height     PageControl1.ActivePage := MapTab;      // Put on first tab      Form1.Caption := 'WSPR Propagation Mapping - V'                      + VERSION + ' --- ' + OpenFileName;      // Set form to 1366x768 for laptop and centered on any larger screen     PixPerInch := Screen.PixelsPerInch;             // Pitch for this PC     sh := Screen.Height;                            // Screen height     sw := Screen.Width;                             // Screen width     ft := ( sh - MINSCRH - TITLEHEIGHT) div 2;      // Centered vertically     if ft < 0 then ft := 0;                         // Don't let it go neg   fl := ( sw - MINSCRW ) div 2;                   // Centered horizontally     if fl < 0 then fl := 0;                         // Don't let it go neg     Form1.Top    := ft;                           // Set top     Form1.Left   := fl;                             // Set left side     Form1.Height := MINSCRH - TITLEHEIGHT;          // Set minimum height     Form1.Width  := MINSCRW;                        // Set minimum width     Application.ProcessMessages;                    // Now do this     {// Start full screen on any PC     Form1.Top    := 0;                              // Set top     Form1.Left   := 0;                            // Set left side     Form1.Height := Screen.Height - TITLEHEIGHT;    // Set to screen height     Form1.Width  := Screen.Width; }                 // Set to screen width      // Intialize the time scale under map     InitWidth := TimeScaleImage.Width;              // Initial width of image     TimeScaleImageInit();     TimeStart := 0;     TimeEnd   := 24;      // Load CallGrid names in config tab and set sizes     CallGridStringGrid.Cells[0,0] := 'Call';     CallGridStringGrid.Cells[0,1] := 'Grid';     CallGridStringGrid.Cells[1,0] := TxCall;        //MY_CALL;     CallGridStringGrid.Cells[1,1] := TxGrid;        //MY_GRID;     CallGridStringGrid.Height     := CallGridStringGrid.DefaultRowHeight                                       * CallGridStringGrid.RowCount                                     + 4;     CallGridStringGrid.Width    := CallGridStringGrid.ColCount                                       * CallGridStringGrid.DefaultColWidth                                     + 4;      // Open users data, call, grid, TZ     ConfigOpen();    // Initialize the station data window     StationImageInit();     StationDataDraw();     TimeScaleDraw( 0, 24 );  end; ---------------------------------------------------------------------- Arctic_Eddie · 2022-08-09 18:13 The whole project zipped is about 100MB as it contains a lot of cached maps and is in debug mode. I can put it on my friends one-drive page and you can get it from there. It will be the one with the highest suffix number. ---------------------------------------------------------------------- wp · 2022-08-09 19:44 Remove the cached maps and I don't the exe, ppu etc either ---------------------------------------------------------------------- Arctic_Eddie · 2022-08-09 20:41 I stripped out the items you mentioned. The program is still complete and there is one spot file that can be opened. The problem routine is near the end of the source file and activated directly using the Test Button at the bottom-left. There are two breakpoints at the problem statements. The third is commented out, TextWidth. The link to my friends public page is below. Select the file on the left with the word "test" in it's name. It's quite small now. There are no cached maps. I did the same F7 with your demo file and it goes to an entirely different place and looks appropriate. There are two folders in the project folder involving MvEngine. I'm not sure how they got there, or are necessary, and may be the problem. https://onedrive.live.com/?authkey=%21AlDtDyEVpMSIPeQ&id=2C53252267529 651%21123&cid=2C53252267529651 ---------------------------------------------------------------------- wp · 2022-08-09 21:11 I could not load the map from VirtualEarth which is used by your MapView component, selected "OpenStreetMap Mapnik" instead. After this fix (and the installation of BGRAControls) the project compiled, but the button click made it crash at the line "APoint.Name := 'Test'". APoint is a local variable of type TGpsPoint - this is a class and must be created! Adding "APoint.Name := TGpsPoint.CreateFrom(rpt)" before "Apoint.Name := 'Test'" solved this issue. The next crash is at line "ADrawer.BrushStyle := ...". Again: ADrawer is a local variable and type TMvCustomDrawingEngine. This is another local variable which has no value. In fact it is the DrawingEngine of the MapViewer (something like a "generalized canvas") needed for drawing on the screen. Assigning "ADrawer := WSPRMap.DrawingEngine" solves the issue, and there is no more crash. The commented TextWidth now is correct too. No text appears on the map, but I already told you that you cannot draw in a button's click event and will not repeat this again. ---------------------------------------------------------------------- Arctic_Eddie · 2022-08-09 22:05 Still having a problem. When I insert "APoint.Name := TGpsPoint.CreateFrom(rpt)", the error says "expected Ansi string". If I change the argument to 'test' then it says "expected TRealPoint". Could you post the corrected version of the routine so I see what you did? Found it, the inserted line should be "APoint := TGpsPoint.CreateFrom(rpt)" without the ".name" part. ---------------------------------------------------------------------- wp · 2022-08-09 22:44 Sorry, this happens when I edit the text already written... ---------------------------------------------------------------------- Arctic_Eddie · 2022-08-09 23:02 I'm going to implement the ideas in your two demos. That will solve my immediate problems. Thanks again for all your help. I could not have sorted this out by myself.