Linuxuser1234 1 Posted January 9, 2023 (edited) trying to get shapefiles to display on a TSphere Component in BOLT.pas(UNIT 1/Form1) from GISEngine.pas i tried this function TGISEngine.RenderShp: boolean; begin GISobj.Viewport3D1.Parent := Earth; end; but that doesnt do anything here is the full unit unit GISEngine; //thank you programmerdelphi2k for helping //This unit Displays Shapefiles onto a 3D Globe interface uses System.SysUtils, System.Classes, FMX.Viewport3D, FMX.Objects3D, System.IOUtils, System.Generics.Collections, BOLT; type TGISFilesnamesSHP = TArray<string>; TGISEngine = class private FFilenames : TList<string>; Viewport3D1: TViewport3D; Earth: TSphere; function GetFilename(const i: integer): string; public constructor Create; overload; constructor Create(const AFilenames: TGISFilesnamesSHP); overload; destructor Destroy; override; procedure AddFilenames(const AFilenames: TGISFilesnamesSHP); procedure ClearFilenames; function CountFilenames: integer; function RenderShp: boolean; property Filename[const i: integer]: string read GetFilename; property Filenames: TList<string> read FFilenames; function VerifyIfAllFilesSHPExists(out AErrors: TGISFilesnamesSHP): boolean; function LoadFilenames: boolean; property GISViewport3D: TViewport3D read Viewport3D1 write Viewport3D1; property GISEarth: TSphere read Earth write Earth; end; var GISobj : TGISEngine; Form1: TForm1; implementation { TGISEngine } constructor TGISEngine.Create; begin Viewport3D1 := TViewport3D.Create(nil); Earth := TSphere.Create(nil); FFilenames := TList<string>.Create; end; constructor TGISEngine.Create(const AFilenames: TGISFilesnamesSHP); begin Create; AddFilenames(AFilenames); end; destructor TGISEngine.Destroy; begin FFilenames.Free; Earth.Free; Viewport3D1.Free; inherited; end; function TGISEngine.GetFilename(const i: integer): string; begin result := ''; if (i > -1) and (i < FFilenames.Count) then result := FFilenames.Items[i]; end; procedure TGISEngine.ClearFilenames; begin FFilenames.Clear; end; function TGISEngine.CountFilenames: integer; begin result := FFilenames.Count; end; procedure TGISEngine.AddFilenames(const AFilenames: TGISFilesnamesSHP); var F: string; begin if (Length(AFilenames) > 0 ) then for F in AFilenames do if not FFilenames.Contains(F) then FFilenames.Add(F); end; function TGISEngine.VerifyIfAllFilesSHPExists(out AErrors: TGISFilesnamesSHP): boolean; var F: string; begin result := false; if (FFilenames.Count = 0) then begin AErrors := ['Filenames list is empty']; exit; end; for F in FFilenames do if not FileExists(F) then AErrors := AErrors + [F + ', dont exists!']; end; function TGISEngine.LoadFilenames: boolean; var F: string; begin result := false; if (FFilenames.Count > 0) then begin for F in Filenames do GISobj.AddFilenames(['tl_2021_01001_roads.shp','world-administrative-boundaries.shp']); end; end; function TGISEngine.RenderShp: boolean; begin GISobj.Viewport3D1.Parent := Earth; end; initialization GISobj := TGISEngine.Create; finalization GISobj.Free; end. Edited January 9, 2023 by Linuxuser1234 Share this post Link to post
programmerdelphi2k 237 Posted January 9, 2023 (edited) hi @Linuxuser1234 Quote function TGISEngine.LoadFilenames: boolean; --> you already load (list of files) in "procedure TGISEngine.AddFilenames(const AFilenames: TGISFilesnamesSHP);" then, to "load", in fact, should be another way on final usage of this class... for example: xxxxx.READmyDATASHPFromFiles; begin for F in FFilenames do MyDataFromFileSHP.readdata( F ); // MyDataFromFileSHP can be a list with each data from each file... if the value is very "big", then, try another way to store it on memory... and release it after usage! note that GISobj is a instance of "TGISEngine", and it dont really do nothing, in fact! try to assing your "Earth" on form where is the "3Dviewport" not in GISObj Edited January 9, 2023 by programmerdelphi2k Share this post Link to post
programmerdelphi2k 237 Posted January 9, 2023 try this way to store your "file names" and "data from each file: uses System.Generics.Collections; type TMyFileNames = TArray<string>; TMyDataFromSHP = string; { just for show the idea } // this type is dependent of "how the data are stored into SHP file", you see? // ... var MyFileNames : TMyFileNames; MyDataFromFileSHP: TDictionary<string, TMyDataFromSHP>; // this can replace your FFilenames=TList<string> begin // create on "Create"-class MyDataFromFileSHP := TDictionary<string, TMyDataFromSHP>.Create; // for var F in MyFileNames do MyDataFromFileSHP.Add(F, 'my data into SHP file'); // destroy it on "Destroy"-class MyDataFromFileSHP.Free; // end; Share this post Link to post
programmerdelphi2k 237 Posted January 9, 2023 Quote function TGISEngine.VerifyIfAllFilesSHPExists(out AErrors: TGISFilesnamesSHP): boolean; change to procedure TGISEngine.VerifyIfAllFilesSHPExists(out AErrors: TGISFilesnamesSHP); // not need result because you are using "Out AErrors", then, if Lenght(AErros)>0 then... exists a error msg! Quote function TGISEngine.LoadFilenames: boolean; ... result := XXXList.Count > 0; // false or true Share this post Link to post
Linuxuser1234 1 Posted January 9, 2023 @programmerdelphi2k is f needed in MyDataFromFileSHP.Add(F,'tl_2021_01001_roads.shp', 'world-administrative-boundaries.shp'); i tried overload but that didnt work Share this post Link to post
programmerdelphi2k 237 Posted January 9, 2023 (edited) you're mixing things up now! or you use your TList<string> or new way TDictionary, look my text above: TMyDataFromSHP = string; { just for show the idea } // this type is dependent of "how the data are stored into SHP file", you see? Quote MyListWithString.Add( 'Filename1.shp' ); // your way // or new way using TDictionary // here you'll have that open your "filename1" and get the data (values into file... if is it possible) for example, if the data is very big, then as said above, you need another way MyDataFromFileSHP.Add('Filename1.shp', 'my data into filename1 is hello world'); // here the values will be "the file content", NOT Filenames list!!! MyDataFromFileSHP.Add('Filename2.shp', 'my data into filename2 is hello world '); First, I dont know the file content is stored: binary, string, etc... open file, read content, store it on MyDataFromFileSHP dictionary! the content is very big, or moderated? Let's use a example: a file1.SHP have its content like a coordenates using in SVG files... then, the content is not big, can be read on memory easely! now, if you read many files SHP in same time, and all together needs a big memory volume, then, this it's not desirable, too! Edited January 9, 2023 by programmerdelphi2k Share this post Link to post
programmerdelphi2k 237 Posted January 9, 2023 What size (average) SHP file do you want to read in your application? What is the content of this file? Is it a binary? Is it some strings? What type of data? How many files do you need to read at any given time? 1, 2, 100, etc.? Share this post Link to post
programmerdelphi2k 237 Posted January 9, 2023 (edited) look this sample using a Dictionary: unit uMyClass; interface uses System.SysUtils, System.Classes, System.Generics.Collections; type TMySHPDataValues = string; TMyDic = TDictionary<string, TMySHPDataValues>; // TMyClass = class // not inherited from other... private FMyFileSHPwithData: TMyDic; // function GetMyCountFiles: integer; function GetMyFileNames: TArray<string>; public constructor Create; destructor Destroy; override; // procedure AddFilesAndData(const AFilename: string); procedure RemoveFilesOnList(const AFilename: string); procedure RemoveAllFilesOnList; // property MyCountFiles: integer read GetMyCountFiles; property MyFileNames: TArray<string> read GetMyFileNames; property MyFilesAndData: TMyDic read FMyFileSHPwithData; end; implementation { TMyClass } constructor TMyClass.Create; begin FMyFileSHPwithData := TMyDic.Create; // TDictionary<string, TMySHPDataValues>.Create; end; destructor TMyClass.Destroy; begin FMyFileSHPwithData.Free; // inherited; end; function TMyClass.GetMyCountFiles: integer; begin result := FMyFileSHPwithData.Count; end; function TMyClass.GetMyFileNames: TArray<string>; begin result := []; // for var F in FMyFileSHPwithData do result := result + [F.Key]; end; procedure TMyClass.RemoveFilesOnList(const AFilename: string); begin if (AFilename <> '') and (FMyFileSHPwithData.Count > 0) then FMyFileSHPwithData.Remove(AFilename); // if not exits, nothing happens! end; procedure TMyClass.RemoveAllFilesOnList; begin FMyFileSHPwithData.Clear; end; procedure TMyClass.AddFilesAndData(const AFilename: string); var MyFileContent: string; begin if (AFilename <> '') { and FileExists(AFilename) } then begin // now, you need open the file and read all content to store it on dictionary... // How are you going to do this? It depends on the content of the file... // // each file will have a content, of course!!! // // ... let's say that content is like a strings: C1, L2, A54, V100, hello, world, etc... MyFileContent := 'C1, L2, A54, V100, hello, world, etc...'; // // if already exists, just replace value FMyFileSHPwithData.AddOrSetValue(AFilename, MyFileContent); end; end; end. implementation {$R *.fmx} uses uMyClass; var MyFilesSHP: TArray<string>; var MyClass: TMyClass = nil; procedure TForm1.Button1Click(Sender: TObject); begin if (MyClass = nil) then exit; // MyClass.RemoveAllFilesOnList; // MyFilesSHP := ['file1.shp', 'file2.shp']; // for var F in MyFilesSHP do MyClass.AddFilesAndData(F); // for var F in MyClass.MyFilesAndData do Memo1.Lines.Add('File: ' + F.Key + ', Content: ' + F.Value); end; procedure TForm1.Button2Click(Sender: TObject); begin if (MyClass <> nil) then Memo1.Lines.AddStrings(MyClass.MyFileNames); end; initialization ReportMemoryLeaksOnShutdown := true; MyClass := TMyClass.Create; finalization MyClass.Free; end. Edited January 10, 2023 by programmerdelphi2k Share this post Link to post