Have an amazing solution built in RAD Studio? Let us know. Looking for discounts? Visit our Special Offers page!
DelphiRAD Studio事例

データサイエンティストが実世界のデータ可視化にDelphiを選択する理由

smartmockups lcuqi4b2

この記事は、データサイエンティストでDelphi開発者のMax Kleiner氏の投稿です。彼は、Maxboxの作者であり、このツールを自身のサイトで「コンパイル済みオブジェクトをベースとしたスクリプトツール」と紹介しています。記事では、RAD Studio / Delphiを用いてWindowsソフトウェアを開発し、HTTPリクエストによって取得したJSONオブジェクトを使って、集積ガス貯蔵施設の貯蔵量データをグラフやチャートで可視化する方法を説明しています。

貯蔵ガスのデータをAGSIデータセットのタイムラインとして可視化

「データサイエンティストにはさまざまな種類がある。その中で最高なのはデータに取り組む人だ。」

このデータサイエンスチュートリアルでは、AGISと呼ばれるデータストレージとタイムラインでの可視化について説明しています。AGISは、Aggregated Gas Storage Inventoryの略で、集積ガス貯蔵施設の貯蔵量のデータです。データプロバイダーから新しいサービスが発表されたり更新があり、Webサイトにその情報が掲載されるたびに、そのデータを最新に保つことが可能になります。

GIE(Gas Infrastructure Europe)は、Storage MapやStorage Investment Databaseといった関連情報も、https://www.gie.eu/publications/maps/ で提供しています。

データチャート(グラフ)の表示

タイムラインデータを可視化したグラフは、以下のようになります。

データのダウンロードとグラフ作成に使用したコンポーネント

このツールでは、WinHttp.WinHttpRequest、JSONObjects、そしてTEEChartsライブラリを使用して、プロットのロードとテストを行っています。APIキーも必要になるため、以下のサイトからあらかじめ入手しておきます。

https://agsi.gie.eu/account

データが表現するもの

取得するデータは、日ごとのガス貯蔵量を表します。データは毎日、19:30と23:00(中央ヨーロッパ時間)に更新されます。コードの詳細を説明する前に、スクリプトの主要な部分を以下に示します。

plotform:= getForm2(1400, 600, clsilver, 'Sciplot4maXbox');
plotform.icon.loadfromresourcename(hinstance,'ZHISTOGRAM');

HttpResponse:= getEnergyStreamJSON2(URL_AGSIAPI2,'DE,2022-01-03,150',AGSI_APIKEY);      
JSON2Plot(plotform, letGenerateJSON2(HttpResponse));

コード概要

コードのメイン部分では、フォームを生成し、API呼び出しとデータのプロットを行っています。

GIEでは、AGSIおよびALSI Transparency Publication Platformsで、API(Application Programming Interface)サービスを提供しています。

APIアクセスを用いれば、ユーザーはAGSIおよびALSIのWebサイトをバイパスして、データを直接かつ継続的に取得できます。Webサイトから各データセットを個別にダウンロードすることなく、必要に応じてデータ抽出、フィルタリング、集計、サブセット生成が可能になります。APIのデータエクスポートフォーマットは、JSONです。以下は、150日分のサブセットデータです。

グラフに描画するデータ

公開されているデータセットは、ACERに提供されているEICコードマッピングテーブルをベースとしています。ストレージデータは、国別、会社別に集計されます。

API呼び出しでは、国、開始日、日数を指定します。

getEnergyStreamJSON2(URL_AGSIAPI2,'DE,2022-01-03,150',AGSI_APIKEY);

データセットは、Excel、CSV,JSON形式でもダウンロードできます。このレポートでは、集計ビューを表示していますが、会社ごと、貯蔵施設ごとの個別データセットにもアクセスできます。

データ取得のためのAPI呼び出し

では、API呼び出しの方法から紹介します。

HttpResponse:= getEnergyStreamJSON2(URL_AGSIAPI2,'DE,2022-01-03,150',AGSI_APIKEY);

このコマンドにより、WinHttp.WinHttpRequestを実行します。失敗すると、以下のような例外が複数生成されます。

Exception: WinHttp.WinHttpRequest: The data necessary to complete this operation is not yet available; or you missed a valid key:

AGSIPost: Failed at getting response:403{
  "error": {
    "code": 403,
    "message": "The request is missing a valid API key.",
    "status": "PERMISSION_DENIED"
  }}

面白いのは、例外がJSON形式になっている点です。

Be careful, also, to expose your key as I get from Git: GitGuardian has detected the following Google API Key exposed within your GitHub account.

APIでJSONデータを取得する

Next is the formatting of the get-call with a valid API-key request in the function energyStream()関数で、有効なAPIキーリクエストを使用してGET呼び出しを書式化します。

function getEnergyStreamJSON2(AURL, feedstream, aApikey:string): string;
   ...
   encodURL:= Format(AURL,[HTTPEncode(asp[0]),(asp[1]),asp[2]]);  
   writeln(encodurl)  //debug     
   hr:= httpRq.Open('GET', encodURL, false);
   httpRq.setRequestheader('user-agent',USERAGENTE);
   httpRq.setRequestheader('x-key',aAPIkey);
   ...

ところで、content-typeはどこで設定するのでしょうか。私の知る限り、content-typeを設定する箇所は、Webリクエストの2箇所のみです。

  1. クライアントが、サーバーに送信するbodyに対して、content-typeを設定(つまり、GETまたはPOSTで設定)
  2. サーバーが、レスポンスに対してcontent-typeを設定

利用可能なContent-Typeを明示する

ペイロードボディを含むメッセージを生成する送信元は、そのcontent-typeが不明でない限り、Content-Typeヘッダフィールドを生成する必要があります。それ以外の場合、503503 - Service Unavailable という応答で取得に失敗します。

('headers={"Content-Type":"application/json"}')
  httpRq.setRequestheader('Content-Type',application/json);

つまり、content-type HTTPヘッダは、PUTおよびPOSTでのみ設定する必要があるということです。GETリクエストでは、クライアントが理解できるコンテンツタイプを示す「Accept」ヘッダを含めることができます。サーバーは、この情報を用いて、返信するコンテンツ タイプを決定できます。

オプションで、TALWinInetHttpClientを用いることもできます。これは、使いやすいWinInetベースのプロトコルで、HTTPSをサポートしています。このHTTPクライアントコンポーネントは、HTTPプロトコルを介してWebでデータの送信および取得ができます。

function TALHTTPClient_Get(aUrl: AnsiString;
                           feedstream, aApikey: string): string;

Var
  LHttpClient: TALWininetHttpClient;
  asp: TStringArray;
begin
  LHttpClient:= TALWininetHttpClient.create;
  asp:= splitStr(feedstream,',');
  LHttpClient.url:= Format(AURL,[HTTPEncode(asp[0]),(asp[1]),asp[2]]);
  LHttpClient.RequestMethod:= HTTPmt_Get; //HTTPrm_Post;
  LHttpClient.RequestHeader.UserAgent:=USERAGENTE;
  //LHttpClient.RequestHeader.CustomHeaders:=
  LHttpClient.RequestHeader.RawHeaderText:='x-key:'+aAPIkey;
  try
    result:= LHttpClient.Get1(LHttpClient.url); //overload;   
  finally
    LHttpClient.Free;
  end;
end;

JSONレスポンスをグラフで使用できるデータに変換する

AGSIでは、欠落したデータも不完全なデータも表示されます。次に行うのは、取得したJSONデータをTJSONObjectを使って、プロット用に変換することです。

function letGenerateJSON2(HttpRqresponseText: string): TJSONArray;
var jo: TJSONObject; 
begin
  jo:= TJSONObject.Create4(HttpRqresponseText);
  try
    //writeln(jo.getstring('data'));
    writeln(itoa(jo.getjsonarray('data').getjsonobject(0).length))
    writeln(itoa(jo.getjsonarray('data').length))
    result:= jo.getjsonarray('data');

    //write out to check    
    for it:= 0 to result.length-1 do
       writeln(result.getjsonobject(it).getstring('gasDayStart')+':'+
                      result.getjsonobject(it).getstring('injection'));
  except
    writeln('EJson: '+ExceptiontoString(exceptiontype, exceptionparam));
  end;                         
end;

関数の戻り値として取得できるこのJSON配列を、次のプロット処理に渡します。

procedure JSON2Plot(form1: TForm; jar: TJSONArray);
var chart1: TChart; 
    cnt: integer; sumup,tmp2,tmp: double; gday: string;
begin
  form1.onclose:= @Form_CloseClick;
  chart1:= ChartInjector(form1);
  sumup:=0; tmp2:=0; tmp:=0;
  try
   for cnt:= 0 to jar.length-1 do begin
     //writeln(locate.getjsonobject(it).getstring('gasDayStart')+':'+
     tmp:= jar.getjsonobject(jar.length-1-cnt).getdouble('injection');
     tmp2:= jar.getjsonobject(jar.length-1-cnt).getdouble('full');
     sumup:= sumup+tmp;
     gday:= jar.getjsonobject(jar.length-1-cnt).getstring('gasDayStart');
     chart1.Series[0].Addxy(cnt,tmp,gday,clgreen);
     chart1.Series[1].Addxy(cnt,tmp2,'',clred);
     chart1.Series[2].Addxy(cnt,jar.getjsonobject(jar.length-1-cnt).getdouble('withdrawal'),'',clyellow);
   end;
  except
    writeln('EPlot: '+ExceptiontoString(exceptiontype, exceptionparam));
  end;  
 PrintF('Landrange %d: Injection sum: %.2f',[jar.length-1,sumup]);
end;

グラフの系列に含まれるデータ

グラフには、4つのデータ系列をプロットします(タイムラインを含む)。

  1. 注入量(ガス日あたり)
  2. 充足率 – Storage / WGV(in%)
  3. 放出量(ガス日あたり – 2桁精度)
  4. ガス日(レポートの開始ガス日)

時系列は、ガス日単位です。

「ガス日」とは、 UTCで5:00~5:00(夏時間では、4:00~4:00)を意味します。ガス日は、ヨーロッパ中央時間(CET)の場合、UTC+1、夏時間(CEST)の場合UTC+2となります(定義: CAM Network Code Specificationsを参照)。

データ取得用APIにアクセスする方法

APIアクセスは、RESTライクなインターフェイス (Representational State Transfer) で提供され、上述したレスポンスヘッダにcontent-typeを含む、JSON形式でデータベースリソースを公開しています。

他言語向けのデータアクセス方法

データサイエンスビジョンのコードには、使用例を示すサンプルが添付されており、Python3、Delphi、Jupyter-Notebook、Object Pascal、maXbox4で使用できます。なお、このAPIサービスは無料公開されています。このプラットフォーム上で現在利用可能なデータのみにアクセスできます。

メモ:システムから直接データを抽出するには、ブラウザで以下のリンクを直接クリックしてください。

https://agsi.gie.eu/api?type=eu

GIEデータAPIに関する情報

GIEでは、AGSIおよびAGSI+ストレージデータに関するAPI(Application Programming Interface)サービスを提供しています。APIドキュメントの作成は進行中であり、サービスの使用方法に関するサンプルやガイダンスも提供しており、APIキーを取得するための登録を行うと利用できます。以下は、過去半年のデータプロットです。

サンプルスクリプト/コードの入手先

スクリプトと画像は、https://github.com/maxkleiner/agsi-dataからダウンロードできます。

ダウンロードコードのサンプル

以下は、maXbox4統合用のWinAPIDownloadクラスからの抜粋です。

TWinApiDownload = class(TObject)
  private
    fEventWorkStart: TEventWorkStart;
    fEventWork: TEventWork;
    fEventWorkEnd: TEventWorkEnd;
    fEventError: TEventError;
    fURL: string;
    fUserAgent: string;
    fStop: Boolean;
    fActive: Boolean;
    fCachingEnabled: Boolean;
    fProgressUpdateInterval: Cardinal;
    function GetIsActive: Boolean;
  public
    constructor Create;
    destructor Destroy; override;
    function CheckURL(aURL: string): Integer;
    function Download(Stream: TStream): Integer; overload;
    function Download(var res: string): Integer; overload;
    function ErrorCodeToMessageString(aErrorCode: Integer): string;
    procedure Stop;
    procedure Clear;
    property UserAgent: string read fUserAgent write fUserAgent;
    property URL: string read fURL write fURL;
    property DownloadActive:Boolean read GetIsActive;
    property CachingEnabled:Boolean read fCachingEnabled write fCachingEnabled;
    property UpdateInterval:Cardinal read fProgressUpdateInterval
                                     write fProgressUpdateInterval;
    property OnWorkStart: TEventWorkStart read fEventWorkStart
                                           write fEventWorkStart;
    property OnWork: TEventWork read fEventWork write fEventWork;
    property OnWorkEnd: TEventWorkEnd read fEventWorkEnd write fEventWorkEnd;
    property OnError: TEventError read fEventError write fEventError;
  end;

参考文献とリンク

RAD Studio 13.1 Florence Now Available See What's New in RAD Studio 13.1 Delphi is 31 - Webinar Replay

Reduce development time and get to market faster with RAD Studio, Delphi, or C++Builder.
Design. Code. Compile. Deploy.

Start Free Trial   Upgrade Today

   Free Delphi Community Edition   Free C++Builder Community Edition

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.

IN THE ARTICLES