Blog
All Blog Posts | Next Post | Previous PostWhen times are hard, tmssoftware makes developing easy
Wednesday, April 8, 2020
First of all we still hope everyone is well and safe in these uncertain times.We at tmssoftware are doing the best we can to give you our best support and new features while we are all working from home. As Billy Ocean sang so wisely: "When the going gets tough, the tough get going". And we keep going, we have a new product TMS FNC Maps.
Today I will show you an example of what you can create with some teamwork between TMS FNC Cloud Pack and TMS FNC Maps. TMS FNC Maps is already available for you as a BETA, if you are an active ALL-ACCESS user.
In case you don't have an ALL-ACCESS subscription, we've created a similar web demo on which you can have an initial look.
Our new product: TMS FNC Maps
Experience the power of dynamically switching between various mapping services with this out of the box, worry free experience that provides mapping, directions and geocoding in one big library that works on 4 frameworks and a lot more operating systems.In case you've missed our announcement, you can find that blogpost here/.
TMS FNC Cloud Pack
TMS FNC Cloud Pack contains components that offer integration with several cloud services. With one source code base to use on multiple frameworks (FMX, VCL, WEB and LCL), this is the best cross-framework option to use your most used REST based services. In case your specific service isn't implemented in the available components, you can easily create your own REST service client components built on top of the TMS FNC Cloud Pack Core.Easy to develop with
To stay informed is one of the most important things right now, so we wanted to give you a useful project. We will wrap an API, that was built by developer Kyle Redelinghuys, which works with the COVID-19 information sourced from Johns Hopkins CSSE. We combine this with TMS FNC Maps to retrieve the geo location from the countries and draw a circle with a radius in relation to the unfortunate deaths in that country. (Note: This image is from the seventh of April.)API Wrapper
To start, we will create our own class so we can personalize the object to our preferences. We start from the TTMSFNCSimpleCloudOAuth class and we add a TObjectList Countries that will contain the data for each country.TTMSFNCCovidAPI = class(TTMSFNCSimpleCloudOAuth) private FCountries: TTMSFNCCloudCovidCountries; public constructor Create(AOwner: TComponent); override; destructor Destroy; override; property Countries: TTMSFNCCloudCovidCountries read FCountries; end;
covid := TTMSFNCCovidAPI.Create(Self); covid.Request.Host := 'https://api.covid19api.com'; covid.Request.Path := '/summary'; covid.ExecuteRequest( (*the anonymous method will be explained later*));
{ "Countries": [ { "Country": "Afghanistan", "CountrySlug": "afghanistan", "NewConfirmed": 2, "TotalConfirmed": 24, "NewDeaths": 0, "TotalDeaths": 0, "NewRecovered": 0, "TotalRecovered": 1 }, { "Country": "Albania", "CountrySlug": "albania", "NewConfirmed": 6, "TotalConfirmed": 70, "NewDeaths": 0, "TotalDeaths": 2, "NewRecovered": 0, "TotalRecovered": 0 } ] }
TTMSFNCCloudCovidCountry = class private FCovidAPI: TTMSFNCCovidAPI; FTotalConfirmed: integer; FNewConfirmed: integer; FSlug: string; FTotalDeaths: integer; FNewDeaths: integer; FCountry: string; FTotalRecovered: integer; FNewRecovered: integer; FCoordinate: TTMSFNCMapsCoordinateRec; public constructor CreateCountry(ACovidAPI: TTMSFNCCovidAPI); destructor Destroy; override; property Country: string read FCountry; property Slug: string read FSlug; property NewConfirmed: integer read FNewConfirmed; property TotalConfirmed: integer read FTotalConfirmed; property NewDeaths: integer read FNewDeaths; property TotalDeaths: integer read FTotalDeaths; property NewRecovered: integer read FNewRecovered; property TotalRecovered: integer read FTotalRecovered; property Coordinate: TTMSFNCMapsCoordinateRec read FCoordinate; end; {$IFDEF WEBLIB} TTMSFNCCloudCovidCountries = class(TObjectList) private function GetItem(Index: Integer): TTMSFNCCloudCovidCountry; procedure SetItem(Index: Integer; const Value: TTMSFNCCloudCovidCountry); public property Items[Index: Integer]: TTMSFNCCloudCovidCountry read GetItem write SetItem; default; end; {$ELSE} TTMSFNCCloudCovidCountries = class(TObjectList<TTMSFNCCloudCovidCountry>); {$ENDIF}
Another thing that might stand out is TTMSFNCMapsCoordinateRec, we will get to that in the TMS FNC Maps part of the demo, but first we will finish our TTMSFNCCovidAPI.
In previous versions we had to parse all of the data ourselves as shown in the following code. But with the new features of TMS FNC Core, this isn't necessary anymore.
procedure TTMSFNCCloudCovidCountry.ParseCountryJSON(AJSON: TJSONValue); var jvv: TJSONValue; begin jvv := TTMSFNCUtils.GetJSONValue(AJSON, 'Country'); if Assigned(jvv) then FCountry := Trim(TTMSFNCUtils.GetJSONProp(AJSON, 'Country')); jvv := TTMSFNCUtils.GetJSONValue(AJSON, 'Slug'); if Assigned(jvv) then FSlug := Trim(TTMSFNCUtils.GetJSONProp(AJSON, 'Slug')); jvv := TTMSFNCUtils.GetJSONValue(AJSON, 'NewConfirmed'); if Assigned(jvv) then FNewConfirmed := TTMSFNCUtils.GetJSONIntegerValue(AJSON, 'NewConfirmed'); jvv := TTMSFNCUtils.GetJSONValue(AJSON, 'TotalConfirmed'); if Assigned(jvv) then FTotalConfirmed := TTMSFNCUtils.GetJSONIntegerValue(AJSON, 'TotalConfirmed'); jvv := TTMSFNCUtils.GetJSONValue(AJSON, 'NewDeaths'); if Assigned(jvv) then FNewDeaths := TTMSFNCUtils.GetJSONIntegerValue(AJSON, 'NewDeaths'); jvv := TTMSFNCUtils.GetJSONValue(AJSON, 'TotalDeaths'); if Assigned(jvv) then FTotalDeaths := TTMSFNCUtils.GetJSONIntegerValue(AJSON, 'TotalDeaths'); jvv := TTMSFNCUtils.GetJSONValue(AJSON, 'NewRecovered'); if Assigned(jvv) then FNewRecovered := TTMSFNCUtils.GetJSONIntegerValue(AJSON, 'NewRecovered'); jvv := TTMSFNCUtils.GetJSONValue(AJSON, 'TotalRecovered'); if Assigned(jvv) then FTotalRecovered := TTMSFNCUtils.GetJSONIntegerValue(AJSON, 'TotalRecovered'); end;
Now all we need is the rest of our ExecuteRequest and all of the data is handled. The complete function looks like this:
covid.ExecuteRequest( procedure(const AResult: TTMSFNCCloudBaseRequestResult) var I: Integer; begin o := TTMSFNCUtils.ParseJSON(AResult.ResultString); if Assigned(o) then begin try jv := TTMSFNCUtils.GetJSONValue(o, 'Countries'); if Assigned(jv) then begin ja := TTMSFNCUtils.GetJSONValue(o, 'Countries') as TJSONArray; for I := 0 to TTMSFNCUtils.GetJSONArraySize(ja) - 1 do begin fo := TTMSFNCUtils.GetJSONArrayItem(ja, i); if Assigned(TTMSFNCUtils.GetJSONValue(fo, 'Country')) and (TTMSFNCUtils.GetJSONProp(fo, 'Country') <> '') then begin country := TTMSFNCCloudCovidCountry.CreateCountry(covid); country.JSON := fo.ToJSON; covid.FCountries.Add(country); if country.TotalDeaths > 0 then begin g.GetGeocoding(country.Country, procedure(const ARequest: TTMSFNCGeocodingRequest; const ARequestResult: TTMSFNCCloudBaseRequestResult) var c: TTMSFNCCloudCovidCountry; begin c := TTMSFNCCloudCovidCountry(ARequest.DataPointer); c.FCoordinate := ARequest.Items[0].Coordinate.ToRec; end, '', country); end; end; end; end; finally o.Free end; end; end );
This might look like a lot, but I will explain the code bit by bit. First of all we define our Anonymous method that will handle the request result, which we parse to JSON.
Then we check if the response has the value 'Countries', otherwise something went wrong with our request. As this is an array of all of the different countries, we will go over them one by one. The check to see if the JSON property for Country is different from an empty string is because we noticed that there was an empty object in the response. But if the Country has a name, then we create a new Country object.
We parse the information in the object and add it to the object list.
A first hand on TMSFNCMaps
For the last part we will move over to TMS FNC Maps. g is an instance of the type TTMSFNCGeocoding. We create this in the FormCreate and add an event to OnRequestsComplete. TTMSFNCGeocoding is a component to perform geocoding of an address or reverse geocoding of a coordinate by using an existing REST API service.g := TTMSFNCGeocoding.Create(Self); g.OnRequestsComplete := DoRequestsComplete; g.APIKey := TMSFNCMaps1.APIKey;
To get back to the Geocoding in our API response. The call for geocoding is as followed:
TTMSFNCCustomGeocoding.GetGeocoding(AAddress: string; ACallback: TTMSFNCGeocodingGetGeocodingCallBack = nil; AID: string = ''; ADataPointer: Pointer = nil);
AAddress: is the country name which we placed in the string Country.country. ACallback: is the anonymous method that we use to store the coordinates in c.FCoordinate. AID: is an empty string as we dont use it. ADataPointer: is the country that we just added and that we convert to c.
That is how we retrieve the coordinates for the countries by their name.
Lets add some things to the map as we would like to have a visible indication to show the impact of the virus, therefor we draw circles on the locations and color them.
This is extremely simple in TTMSFNCMaps with the AddCircle method where you just need to give the circle center and the radius.
We do this in the DoRequestsComplete event that is triggered when our geo location is done.
procedure TForm1.DoRequestsComplete(Sender: TObject); var I: Integer; cl: TTMSFNCGraphicsColor; begin TMSFNCMaps1.BeginUpdate; for I := 0 to covid.Countries.Count - 1 do begin with TMSFNCMaps1.AddCircle(covid.Countries[I].Coordinate, Max(50000, 25 * covid.Countries[I].TotalDeaths)) do begin DataPointer := covid.Countries[I]; cl := gcSeagreen; if covid.Countries[I].TotalDeaths > 500 then cl := gcOrange; if covid.Countries[I].TotalDeaths > 1000 then cl := gcRed; FillColor := cl; Strokecolor := cl; FillOpacity := 0.5; end; end; TMSFNCMaps1.EndUpdate; end;
The last thing we will add in this example are some events of TTMSFNCMaps. To be more precise we will show a pop-up when we hover the circles that will show the total numbers we retrieved from the API. We do this in the event OnPolyElementMouseEnter.
With the country that was set as datapointer in DoRequestsComplete, we can easily get the different data in the event.
We just call the ShowPopUp method with the location and the text we want to show and as you can see in the example we can use HTML for this as well.
procedure TForm1.TMSFNCMaps1PolyElementMouseEnter(Sender: TObject; AEventData: TTMSFNCMapsEventData); var c: TTMSFNCCloudCovidCountry; begin if Assigned(AEventData) then begin c := AEventData.PolyElement.DataPointer; TMSFNCMaps1.ShowPopup((AEventData.PolyElement as TTMSFNCMapsCircle).Center.ToRec, '', 0, 0); end; end; procedure TForm1.TMSFNCMaps1PolyElementMouseLeave(Sender: TObject; AEventData: TTMSFNCMapsEventData); begin TMSFNCMaps1.CloseAllPopups; end;' + c.Country + 'Contaminations: ' + c.TotalConfirmed.ToString + 'Deaths: ' + c.TotalDeaths.ToString + 'Recovered: ' + c.TotalRecovered.ToString + '
We didnt use the most valuable feature of TMSFNCMaps, but in case Google Maps is down, we can easily change for another service like Bing, Azure, Here, TomTom, MapBox or OpenLayers to keep the application up-and-running.
This example shows how easy it is to use two very powerful TMS FNC products. And we will go on and try to amaze you with new products and features that are very user-friendly.
Stay safe and remember: When the going gets rough, the tough get rough.
Gjalt Vanhouwaert
This blog post has received 2 comments.
2. Thursday, April 9, 2020 at 8:10:47 AM
Well done. Am very pleased to see that development is still continuing in spite of this current crisis!
Randall Ken
All Blog Posts | Next Post | Previous Post
Price Rhett