Reaching the browser page with TeeChart

How different versions of TeeChart reach the browser page

 

Recent news that Microsoft are ceasing support for ActiveX with the new navigator, Edge, included with Windows 10, seems to signal the end of mainstream browser support for ActiveX.

ActiveX support was one of the flagship functionality elements for Internet Explorer some years ago but security policies of many organisations, in response to reports of possible intruder exploitation of some aspects of some ActiveX controls, have meant that many applications using ActiveX components have shifted off the browser completely or that browser apps have moved to different formats such as ASP.NET, Java Applets or Javascript/HTML5.

Security clearance for TeeChart ActiveX Control in Internet Explorer

Quite some time ago Steema Software worked extensively with some key customers and CERT (https://www.us-cert.gov/) certification, using their recommendations and analysis tools to make sure that any possible entry points to TeeChart were closed-off. As such, here at Steema we don’t consider TeeChart to offer security weakpoints, but nevertheless, the industry has steadily moved away from ActiveX on the browser and alternative options already needed to be studied as far back as multiple years ago.

This article discusses some of the current options available.

Static Charts on the browser page

All versions of TeeChart are able to work as a serverside component and to generate a static, rendered image of a chart in response to any dynamic input, request or web form post. The response can be quick and effective. Though a static chart offers no interactivity to the person using the application, interactivity is not always necessary or desirable and a static chart has its place as a valid browser page format.

Interactive Charts on the browser page

Interactive Charts on the browser page permit persons using the application to work with the chart, changing its view via a zoom or scroll, or permitting a click/touch on a chart to return more information or insight or to provoke a drill-down in search of more detailed information.

The following sections describe what each version of TeeChart has to offer in terms of interactive charting functionality on the browser.

 

TeeChart for .NET – ASP.NET and WebForm applications

TeeChart for .NET offers a WebChart for placement on a Webform.  The chart is editable via the chart editor at designtime in the Visual Studio IDE and can be run with several interactivity modes:

  • Static Chart; the rendering of a png, jpg or gif format Chart in response to an ASP.NET page query. No interactivity experience for the user other than that offered by external options around the chart such as a combo selection to generate a new chart or view.
  • Interactive Ajax powered Javascript Chart; interaction for mouseclicks locally on page or for drilldown (postback) to server. Charts can be zoomed by mouse or scrolled via a scrollbar. Javascript on the page permits a variety of enhancements to affect how the chart reacts to interaction. User experience for interaction at this level is of a moderate to good level.
  • Interactive HTML5 Canvas powered Chart. The designtime editing options remain the same as for previous options but the chart is rendered on a fully interactive HTML5 Javascript powered Canvas that allows pageside javascript to control some aspects of the charting experience and for others to be fed back to the serverside module. Not all TeeChart for .NET’s Series types can be rendered to the HTML5. User experience in terms of interactivity is of a very high level as the HTML5 chart is a live component on the page with full access at runtime to the TeeChart for Javascript charting library.

 

TeeChart for Javascript, HTML5 applications

TeeChart for Javascript is a pure-for-the-web-browser designed Component. This version of TeeChart, also referred to as TeeChart for HTML5, requires to be designed by an HTML page designer with facility to edit on-page Javascript (notepad will do) and offers a very high level of interactivity to the browser page viewer as the chart is fully live in the page with dynamic access to the full TeeChart for HTML5 component library. HTML5 charts can reach virtually any platform as nearly all current browsers natively support Javascript and the HTML5 canvas.

 

TeeChart in HTML5. Live zoom/scroll/click
TeeChart for Javascript/HTML5.

TeeChart for Java, Servlet and Applet applications

TeeChart for Java powered web browser applications may be viewed on the browser page as a Java applet. A Java applet is, in effect, a standalone application on the browser page and offers functionality and a way of working, similar in many respects to the TeeChart ActiveX control. Applet support in the browser is in decline and appears to be going the same way as ActiveX Control support as many organisations add restrictions to the technology types they are willing to support on browser platforms. More recent versions of browsers have imposed further security restrictions demanding code-signing of controls that are allowed to be run on the browser page. If you decide to start a new project using Java Applets as the means to take TeeChart to the browser page, we recommend you check first, the current situation for browser support and certification requirements.

A Java servlet runs at the server and is able to return static Charts as images to client browser applications in response to dynamic requests.

 

TeeChart for VCL

TeeChart for VCL can reach the browser with a fully interactive Chart via Delphi’s ActiveForm technology. As ActiveForm is an ActiveX control, support for it will be dropped from the Edge browser included with Windows 10 so we recommend you confirm the browser platform to be used before starting new development projects for that environment with it.

TeeChart for VCL also supports export to HTML5, thus offering fully live charts on the browser page. Export is supported for a limited set of Series types only.

 

TeeChart for PHP

TeeChart for PHP is designed to respond to dynamic requests at a web server to provide charts in static or dynamic formats. The current supported dynamic format is HTML5 and TeeChart for PHP works in unison with TeeChart for HTML5 to create a javascripted clientside, fully interactive chart.

 

TeeChart for ActiveX, ASP applications

TeeChart for ActiveX has been discussed at the opening of this article. It is a very powerful way to deliver a web application to a browser, being similar in nature to a desktop application on the browser page. ActiveX has only ever been supported on Internet Explorer (apart from some addins available in the past for other browsers) which limits its public a little. The phasing out of support for ActiveX for the default browser, Edge, in Windows 10 means that it is probably best avoided when considering developing new browser applications going forward. TeeChart for ActiveX remains an extremely useful tool for virtually all other COM compatible containers and can be used to create static charts for the browser page with javascript enhancements for hotspots.

Alternatively the TeeChart for ActiveX version may still be used to generate live, interactive charts in HTML5 for virtually all browsers. Note that HTML5 export is supported for a limited set of Series types only.

Delphi Gestures with TeeChart

Since Embarcadero introduced touch screen support, the way gestures are being handled has evolved. Now that we are in the RAD Studio XE8 days, it has become pretty straightforward as documented in their “Gesturing Overview” article.

DelphiGestures

That article lays out the foundation on how to work with touch gestures and controls. Applying that to TeeChart, means we’ll need TChart and TGestureManager components. TGestureManager, which manages all the gestures that can be used by the control, will have to be associated to TChart’s Touch property. There you can choose which gestures will be associated with the control. There are three kinds of gestures: standard, custom and interactive. The example discussed is based on Delphi’s interactive gestures example.

Here you can download the full project used for the article. Now I’ll explain how to create it. Before starting to write code, please do the following at design-time: add a TChart component, add a TGestureManger component, passing the gesture manager to TChart‘s Touch property and enable Zoom, Pan and DoubleTap interactive gestures on it.

Once this is done, it’s time to start typing code. First of all we’ll deactivate some TeeChart standard interactions so they don’t interfere with the functionality gestures will implement. So we will disable default zoom and panning in form’s OnCreate event:

procedure TForm1.FormCreate(Sender: TObject);
var
  Series1: TSurfaceSeries;
begin
  Series1 := TSurfaceSeries.Create(Self);
  Series1.FillSampleValues(10);
  Series1.UseColorRange := False;
  Series1.UsePalette := True;
  Series1.PaletteStyle := psStrong;

  Chart1.AddSeries(Series1);
  Chart1.Zoom.Allow := False;
  Chart1.Panning.Active := False;
  Chart1.Chart3DPercent := 50;

  with TFlatTheme.Create(Chart1) do
  try
    Apply;
  finally
    Free;
  end;
end;

After that, it’s the turn of TChart‘s OnGesture event implementation:

procedure TForm1.Chart1Gesture(Sender: TObject;
  const EventInfo: TGestureEventInfo; var Handled: Boolean);
begin
  if EventInfo.GestureID = igiZoom then
    handleZoom(EventInfo)
  else if EventInfo.GestureID = igiPan then
    handlePan(EventInfo)
  else if EventInfo.GestureID = igiDoubleTap then
    handleDoubleTap(EventInfo);

  Handled := True;
end;

We are checking for TInteractiveGestures gestures performed on the chart, using event’s TGestureEventInfo, and implement a specific gesture handler method for each one. Finally, we set the Handled parameter to True so that the event is not propagated further.

Let’s speak about gesture handler methods now, starting with zoom:

procedure TForm1.handleZoom(EventInfo: TGestureEventInfo);
var
  LObj: IControl;
  chart: TChart;
  zoom: Double;
begin
  LObj := Self.ObjectAtPoint(ClientToScreen(EventInfo.Location));
  if LObj is TChart then
  begin
    if not(TInteractiveGestureFlag.gfBegin in EventInfo.Flags) then
    begin
      chart := TChart(LObj.GetObject);
      zoom := (EventInfo.Distance / FLastDIstance) * chart.Aspect.ZoomFloat;
      chart.Aspect.ZoomFloat := Max(10, zoom);
    end;
  end;
  FLastDIstance := EventInfo.Distance;
end;

Here we are implementing something different and simpler than the standard zoom in TeeChart. It’s based on the difference between the current distance and pinch that the gesture provides and the distance saved from previous calls, not allowing a zoom factor smaller than 10% of the original size.

Let’s continue with the pan gesture which, in this example, will be used for rotating the chart instead of panning it:

procedure TForm1.handlePan(eventInfo: TGestureEventInfo);
var
  LObj: IControl;
  chart: TChart;
begin
  LObj := Self.ObjectAtPoint(ClientToScreen(EventInfo.Location));
  if LObj is TChart then
  begin
    if not(TInteractiveGestureFlag.gfBegin in EventInfo.Flags) then
    begin
      chart := TChart(LObj.GetObject);

      chart.Aspect.Orthogonal := False;
      chart.Aspect.RotationFloat := chart.Aspect.RotationFloat + (EventInfo.Location.X - FLastPosition.X);
      chart.Aspect.ElevationFloat := chart.Aspect.ElevationFloat - (EventInfo.Location.Y - FLastPosition.Y);
    end;

    FLastPosition := EventInfo.Location;
  end;
end;

Similar to the pinch zoom gesture, here displacement (calculated from the screen position) is being used to rotate and elevate the chart.

Finally, the double tap gesture:

procedure TForm1.handleDoubleTap(eventInfo: TGestureEventInfo);
var
  LObj: IControl;
begin
  LObj := Self.ObjectAtPoint(ClientToScreen(EventInfo.Location));
  if LObj is TChart then
    ResetChart(TChart(LObj.GetObject));
end;

procedure TForm1.ResetChart(chart: TChart);
begin
  chart.Aspect.Orthogonal := True;
  chart.Aspect.ZoomFloat:=100;
  chart.Aspect.ElevationFloat:=345;
  chart.Aspect.RotationFloat:=345;
end;

It’s only used for resetting chart properties to their original values.

I hope this example is useful to illustrate the possibilities TeeChart has with multi-touch gesture on touch devices. It only covers a few cases but this opens up the possibility to a new world of charting interactions.

Here’s the complete code listing for the example discussed in this article:

unit Unit1;

interface

uses
  System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
  FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs,
  FMX.Controls.Presentation, FMX.StdCtrls, FMXTee.Engine, FMXTee.Procs,
  FMXTee.Chart, FMXTee.Series, FMXTee.Commander, FMX.Gestures,
  FMXTee.Series.Surface, FMXTee.Themes;

type
  TForm1 = class(TForm)
    Chart1: TChart;
    GestureManager1: TGestureManager;
    procedure FormCreate(Sender: TObject);
    procedure Chart1Gesture(Sender: TObject; const EventInfo: TGestureEventInfo;
      var Handled: Boolean);
  private
    { Private declarations }
    FLastPosition: TPointF;
    FLastDistance: Integer;
    procedure handleZoom(eventInfo: TGestureEventInfo);
    procedure handlePan(eventInfo: TGestureEventInfo);
    procedure handleDoubleTap(eventInfo: TGestureEventInfo);
    procedure ResetChart(chart: TChart);
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.fmx}

uses System.Math;

procedure TForm1.Chart1Gesture(Sender: TObject;
  const EventInfo: TGestureEventInfo; var Handled: Boolean);
begin
  if EventInfo.GestureID = igiZoom then
    handleZoom(EventInfo)
  else if EventInfo.GestureID = igiPan then
    handlePan(EventInfo)
  else if EventInfo.GestureID = igiDoubleTap then
    handleDoubleTap(EventInfo);

  Handled := True;
end;

procedure TForm1.FormCreate(Sender: TObject);
var
  Series1: TSurfaceSeries;
begin
  Series1 := TSurfaceSeries.Create(Self);
  Series1.FillSampleValues(10);
  Series1.UseColorRange := False;
  Series1.UsePalette := True;
  Series1.PaletteStyle := psStrong;

  Chart1.AddSeries(Series1);
  Chart1.Zoom.Allow := False;
  Chart1.Panning.Active := False;
  Chart1.Chart3DPercent := 50;

  with TFlatTheme.Create(Chart1) do
  try
    Apply;
  finally
    Free;
  end;
end;

procedure TForm1.handleDoubleTap(eventInfo: TGestureEventInfo);
var
  LObj: IControl;
begin
  LObj := Self.ObjectAtPoint(ClientToScreen(EventInfo.Location));
  if LObj is TChart then
    ResetChart(TChart(LObj.GetObject));
end;

procedure TForm1.handlePan(eventInfo: TGestureEventInfo);
var
  LObj: IControl;
  chart: TChart;
begin
  LObj := Self.ObjectAtPoint(ClientToScreen(EventInfo.Location));
  if LObj is TChart then
  begin
    if not(TInteractiveGestureFlag.gfBegin in EventInfo.Flags) then
    begin
      chart := TChart(LObj.GetObject);

      chart.Aspect.Orthogonal := False;
      chart.Aspect.RotationFloat := chart.Aspect.RotationFloat + (EventInfo.Location.X - FLastPosition.X);
      chart.Aspect.ElevationFloat := chart.Aspect.ElevationFloat - (EventInfo.Location.Y - FLastPosition.Y);
    end;

    FLastPosition := EventInfo.Location;
  end;
end;

procedure TForm1.handleZoom(EventInfo: TGestureEventInfo);
var
  LObj: IControl;
  chart: TChart;
  zoom: Double;
begin
  LObj := Self.ObjectAtPoint(ClientToScreen(EventInfo.Location));
  if LObj is TChart then
  begin
    if not(TInteractiveGestureFlag.gfBegin in EventInfo.Flags) then
    begin
      chart := TChart(LObj.GetObject);
      zoom := (EventInfo.Distance / FLastDIstance) * chart.Aspect.ZoomFloat;
      chart.Aspect.ZoomFloat := Max(10, zoom);
    end;
  end;
  FLastDIstance := EventInfo.Distance;
end;

procedure TForm1.ResetChart(chart: TChart);
begin
  chart.Aspect.Orthogonal := True;
  chart.Aspect.ZoomFloat:=100;
  chart.Aspect.ElevationFloat:=345;
  chart.Aspect.RotationFloat:=345;
end;

end.

Check out the new StandardSeriesDemo example

Check out the new StandardSeriesDemo example which has been created for most of the Steema TeeChart products.

You can download and check the source code for each TeeChart version at the Steema GitHub page :

https://github.com/Steema/TeeChart-for-.NET-CSharp-WinForms-samples/tree/master/StandardSeriesDemo (for TeeChart for NET WinForms -CSharp).

https://github.com/Steema/TeeChart-for-FireMonkey-applications-samples/tree/master/StandardSeriesDemo (for TeeChart for FireMonkey FMX).

https://github.com/Steema/TeeChart-ActiveX-samples/tree/master/StandardSeriesDemo (for TeeChart ActiveX).

https://github.com/Steema/teechart-xamarin-foms-samples/tree/master/StandardSeriesDemo (for TeeChart for Xamarin.Forms).

https://github.com/Steema/TeeChart-Java-for-Swing-applications-samples/tree/master/StandardSeriesDemo (for TeeChart for Java).

StandardSeriesDemo1 StandardSeriesDemo2 StandardSeriesDemo3 StandardSeriesDemo4

 

Generic Delphi Tree structure

A generic Tree structure made in Delphi is freely available at this GitHub repository:

https://github.com/davidberneda/GenericTree

See the readme at GitHub for details and updated documentation.

This small class allows creating hierarchical structures where each “node” is a small object (20 bytes plus your own data size), containing a property of your desired type.

For example, a Tree of string nodes:

var MyTree : TNode<String>;

MyTree.Data :=’hello’;

 

 

 

Clustering visualization

TeeChart Pro includes classes and components to perform “clustering” on your data, and optionally visualize the results using a chart “Tool” component.

Clustering is the process of grouping data automatically, according to how well related are the individual items.

As an unsupervised algorithm, its widely used in data-mining / machine-learning / B.I. (Business Intelligence) applications.

 

teechart_clustering

For more information on clustering visit the following Wikipedia link: http://en.wikipedia.org/wiki/Cluster_analysis

An executable example:

http://www.steema.us/files/public/teechart/vcl/demos/clustering/TeeChart_Clustering.zip

The clustering algorithm can be processed on custom data, not necessarily on TeeChart “Series” data.

 

Classes and Units

The TeeClustering.pas unit, for both VCL and Firemonkey, contains abstract “engine” classes that perform the clustering algorithms.

Three different clustering methods are provided:

• TKMeansClustering

• THierarchicalClustering

• TQTClustering (Quality Threshold)

These classes derive from a common abstract class: TBaseClustering.

Each clustering method has its own properties that determine how will the clusters be calculated. After calculating, you can access the Clusters property, which is a TList of TCluster objects.

A TCluster contains child clusters (Items[ ]), so you can check which input data items belong to which cluster, or in the case of the Hierarchical type, access the tree structure (clusters and sub-clusters).

The input data (your data) is not contained by the above classes.

Data is passed to the clustering engine through a “provider” class. There is currently one kind of data provider (TSeriesProvider) to cluster XY or XYZ Series points.

This class is implemented in the TeeClusteringTool.pas unit, together with a charting Tool class (TClusteringTool) to make things easier and more automatic.

 

Basic Example

Example runtime code (it can be done at design-time too, without coding) :

uses TeeClusteringTool;

var tool : TClusteringTool;

tool:=TClusteringTool.Create(Self);

tool.ParentChart:=Chart1;

tool.Series:=Series1; // your series

tool.Method:=cmKMeans;

tool.KMeans.NumClusters:=5;

tool.Execute;

 

After execution, you can loop on the resulting output clusters, for example:

var t : Integer;

for t:=0 to tool.Clusters.Count-1 do
Memo1.Lines.Add( ‘Cluster: ‘+IntToStr(t)+’ contains:  ‘+
IntToStr(tool.Clusters[t].Count)+’ points’ );

 

 

TClusteringTool

This tool automatically performs clustering using the choosen method and parameters, and optionally paints each source series point with a different color indicating which cluster they belong to, and/or draws polygons around each group of cluster’s items, among other things.

Properties:

ClusteringTool1.Method := cmHierarchical;

ClusteringTool1.ColorEach := True; // paint Series with one color per cluster

ClusteringTool1.ShowBounds := True; // draws convex polygons bounding each cluster points

ClusteringTool1.Centers.Visible := True; // shows cluster centers

ClusteringTool1.Centroids.Visible := True; // shows cluster centroids

Other properties include Brush, Pen and Transparency, used when drawing cluster polygon boundaries.

Methods:

Several helper methods are provided:

// Obtain cluster’s center and centroid XY points in Series scales:

var P : TPointFloat;

P:=ClusteringTool1.GetClusterCenter( ClusteringTool1.Clusters[3] );

P:=ClusteringTool1.GetClusterCentroid( ClusteringTool1.Clusters[2] );

// Obtain an array of XY points (in screen pixel coordinates), that belong to cluster:

var PP : TPointArray;

ClusteringTool1.GetClusterPoints( ClusteringTool1.Clusters[4], PP);

PP:=nil;

// Get cluster statistics:

var S : TClusterStats;

S:=ClusteringTool1.GetStats( ClusteringTool1.Clusters[0] );

 

Calculation parameters

Each clustering algorithm needs different parameters:

K-Means:

ClusteringTool1.KMeans.NumClusters := 10; // Number of minimum clusters (“K”)

ClusteringTool1.KMeans.MaxIterations := 1000; // Maximum number of iterations before stopping

Hierarchical:

ClusteringTool1.Hierarchical.NumClusters := 8; // Number of tree root clusters

QT:

ClusteringTool1.QTClustering.MinCount := 30; // Minimum number of points to form a cluster

ClusteringTool1.QTClustering.MaxDiameter := 100; // Maximum “diameter” a cluster can grow

 

Common parameters:

Distance

Cluster calculation is based on the “distance” between a data item and the other data items. There are several ways to calculate the “distance” between items.

The algorithms are agnostic, they call the Provider (ie: Series provider) to obtain the distances.

For example, on a XY scatter plot, the distance between points can be for example the hypotenuse (Pythagoras’ theorem), that is, the simple Euclidean distance between a point XY and another XY.

Distance calculations implemented:

dmEuclidean
dmSquaredEuclidean
dmManhattan
dmMinkowski
dmSorensen
dmChebyshev

 

Example:

ClusteringTool1.Distance := dmMinkowski;

ClusteringTool1.MinkowskiLambda := 4;

 

Linkage

There are several ways to calculate the “distance” between clusters when one or the two clusters have more than one item.

This is called “linkage”.

The most simple way is using each cluster “center” (this means no linkage occurs).

Other linkage styles implemented:

lmSingle

Also called “minimum”.

The distance between cluster A and B is the minimum distance between all items in cluster A and all items in cluster B.

lmComplete

Also called “maximum”.

The distance between cluster A and B is the maximum distance between all items in cluster A and all items in cluster B.

lmAverage

The distance between cluster A and B is the average distance between all items in cluster A and all items in cluster B.

lmWard

The result is the increase on “error sum of squares” when adding cluster B items to cluster A.

 

Calculation speed

Clustering is a slow process by nature. Each clustering method has different performance bottlenecks, proportional to the number of input data items.

The TeeClustering.pas unit has been greatly fine-tuned to optimize the speed of each algorithm, although much work is needed to find more advanced techniques that require less CPU cycles.

The QT Threshold algorithm benefits of parallelism, when multiple CPUs can be used together.

Speed examples (revisited):

(Time in milliseconds, Windows 8.1 x64 on Intel i7 4770 @ 3.4Ghz)

IDE XE8 Delphi, Win32, 5000 data points

Algorithm      Single CPU   Multiple CPU

K-Means              47                     31
Hierarchical    4328                4156
QT                     2859                  703

Notes:

x64 bit executables are a little bit faster than 32bit.

Speed is also very dependant on the “distance” calculation method that is used to compare data.  The default Euclidean calculation has a quite big CPU cost as it calculates the Hypotenuse between two data XY value pairs.