We would like to draw a line at maximum value on a chart - teechart

We are using FastReport tool for reporting.
On this report there is a teechart where we would like to draw a line at maximum value of some bar charts.
We have tried to solve the problem with adding new series but the problem is that the line doesn't start at x = 0.
How can we achieve that there would be a line at maximum values starting at x = 0 (right after y axis).
Example with series, not starting at x=0
Example of code for drawing the line:
void __fastcall TFTedPoro::cxButton2Click(TObject *Sender)
{
double XMin, XMax, YVal;
frxReport1->LoadFromFile("porocilo.fr3");
TfrxChartView *cv;
cv = (TfrxChartView *)frxReport1->FindObject("Chart1");
if (cv != NULL)
{
cv->Chart->Series[0]->Marks->Visible = false;
cv->Chart->Series[1]->Marks->Visible = false;
cv->Chart->Series[2]->Marks->Visible = false;
cv->Chart->Series[3]->Marks->Visible = false;
cv->Chart->Series[4]->Marks->Visible = false;
cv->Chart->Series[5]->Marks->Visible = false;
cv->Chart->Series[6]->Marks->Visible = false;
if (max_vred >= 350)
{
cv->Chart->LeftAxis->Maximum = max_vred;
}
}
XMin = cv->Chart->Series[0]->XValues->MinValue;
XMax = cv->Chart->Series[0]->XValues->MaxValue;
YVal = cv->Chart->Series[0]->YValues->MaxValue;
for (int i = 1; i < cv->Chart->SeriesCount()-1;i++)
{
XMin = Min(XMin, cv->Chart->Series[i]->XValues->MinValue);
XMax = Max(XMax, cv->Chart->Series[i]->XValues->MaxValue);
YVal = Max(YVal, cv->Chart->Series[i]->YValues->MaxValue);
}
cv->Chart->Series[8]->AddXY(XMin-1, YVal);
cv->Chart->Series[8]->AddXY(XMax+1, YVal);
cv->Chart->Axes->Bottom->SetMinMax(XMin-0.5, XMax+0.5);
frxReport1->ShowReport(true);
}
The result looks like this:
Chart with code on button click

Use a TColorLineTool as in the example in the Features Demo:
The Features Demo is shipped with TeeChart VCL evaluation version:
Update:
If you don't have access to the Chart Tools, another approach would be using a TLineSeries with two values.
Note you can set a first XValue smaller to the smallest XValue in your other series, and a second XValue bigger than the biggest XValue in your other series. Then, you can manually set the bottom axis Minimum and Maximum (ie through SetMinMax(min, max) function) to only fit the points in your original series.
Here it is a simple example:
uses Series, Math;
procedure TForm1.FormCreate(Sender: TObject);
var i: Integer;
XMin, XMax, YVal: double;
begin
for i:=0 to 7 do
begin
with Chart1.AddSeries(TBarSeries) as TBarSeries do
begin
Marks.Visible:=false;
FillSampleValues();
end;
end;
XMin:=Chart1[0].XValues.MinValue;
XMax:=Chart1[0].XValues.MaxValue;
YVal:=Chart1[0].YValues.MaxValue;
for i:=1 to Chart1.SeriesCount-1 do
begin
XMin:=Min(XMin, Chart1[i].XValues.MinValue);
XMax:=Max(XMax, Chart1[i].XValues.MaxValue);
YVal:=Max(YVal, Chart1[i].YValues.MaxValue);
end;
with Chart1.AddSeries(TLineSeries) as TLineSeries do
begin
AddXY(XMin-1, YVal);
AddXY(XMax+1, YVal);
end;
Chart1.Axes.Bottom.SetMinMax(XMin-0.5, XMax+0.5);
end;
This is how the example above looks for me here:
A third alternative would be to manually draw an horizontal line at OnAfterDraw. Ie:
procedure TForm1.Chart1AfterDraw(Sender: TObject);
var Ythreshold: Double;
tmpY: Integer;
begin
Ythreshold:=Chart1[0].YValues.MaxValue;
tmpY:=Chart1.Axes.Left.CalcPosValue(Ythreshold);
Chart1.Canvas.Pen.Color:=clRed;
Chart1.Canvas.Line(Chart1.Axes.Bottom.IStartPos, tmpY, Chart1.Axes.Bottom.IEndPos, tmpY);
end;

Related

amchart scatterplot change background of one quadrant

Here I tried something like this but, It is colouring entire left side to right from 0 to 50.
function colourQuadrantX(start, end) {
var range = valueAxisX.axisRanges.create();
range.value = start;
range.endValue = end;
range.axisFill.fill = am4core.color("#396478");
range.axisFill.fillOpacity = 0.2;
range.grid.strokeOpacity = 0;
}
Please take a look how can solve this

How do I print out on a piece of paper with a button click [closed]

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
This question does not appear to be about programming within the scope defined in the help center.
Closed 2 years ago.
Improve this question
I'm searching for a solution on printing out a page using a button OnClick event.
Let me give you an example:
With a button OnClick event, the code will randomly place certain images next to each other. With every click, I want those pictures (either the same or a different button) printed out on a piece of paper.
I tried searching for any connection with Delphi and printer in toolbar, but had no luck.
In principle, it is very easy to print using Delphi. You basically draw on the page's canvas as you'd draw on an on-screen canvas using the VCL (that is, using the Windows GDI).
Here is a very simple example:
procedure PrintRects;
const
Offset = 100;
RectCountY = 8;
RectCountX = 4;
var
S: string;
TitleRect: TRect;
MainRect: TRect;
j: Integer;
i: Integer;
RectWidth,
RectHeight: Integer;
R: TRect;
function GetRectRect(X, Y: Integer): TRect;
begin
Result := Rect(
MainRect.Left + X * RectWidth,
MainRect.Top + Y * RectHeight,
MainRect.Left + (X + 1) * RectWidth,
MainRect.Top + (Y + 1) * RectHeight
);
end;
begin
with TPrintDialog.Create(nil) do
try
if not Execute then
Exit;
finally
Free;
end;
Printer.BeginDoc;
try
Printer.Canvas.Font.Size := 42;
S := 'My Collection of Rects';
TitleRect := Rect(
Offset,
Offset,
Printer.PageWidth - Offset,
Offset + 2 * Printer.Canvas.TextHeight(S)
);
MainRect := Rect(
Offset,
TitleRect.Bottom + Offset,
Printer.PageWidth - Offset,
Printer.PageHeight - Offset
);
RectWidth := MainRect.Width div RectCountX;
RectHeight := MainRect.Height div RectCountY;
Printer.Canvas.TextRect(TitleRect, S, [tfSingleLine, tfCenter, tfVerticalCenter]);
for j := 0 to RectCountY - 1 do
for i := 0 to RectCountX - 1 do
begin
R := GetRectRect(i, j);
Printer.Canvas.Brush.Color := RGB(Random(255), Random(255), Random(255));
Printer.Canvas.FillRect(R);
end;
finally
Printer.EndDoc;
end;
end;
This produces the following page:
Needless to say, instead of solid-colour rectangles, you could print your images here in this grid.
Hence, if you know how to draw things on a form (using TCanvas, that is, Windows GDI), you can use the same methods to draw on a printed page.
And of course, you can call this procedure when you click a button:
procedure TForm1.Button1Click(Sender: TObject);
begin
PrintRects;
end;

How to show pop-up window animation frame (starting from a grid cell rect)

I'm using BDS 2007...
When a user double-clicks a grid cell in our application we display a modal window over the grid. I developed the routine below to draw an animation frame on the grid. It enlarges by drawing and erasing a frame in steps, before the window shows (much like the Windows UI animation). The same routine gets called again (with bExpand=False) after the window closes, to give the UI effect of the window imploding back into the grid cell.
This routine worked fine in Windows XP but misbehaves when Aero is enabled (works fine in Windows 7 without Aero). The animation frame redraws slowly...even if I comment out the DelayMSecs line (which is just a loop that calls Sleep(0) repeatedly until the iDelay number of milliseconds has passed).
And when the routine is called again (after the window closes) it is equally slow, plus leaves a frame on the screen, plus there's a ghost image of the (now closed) window left in place, essentially alpha-blended with the normal display contents of the grid.
Most of the routine's code calculates the changing rectangle size. Only three lines do the actual draw & erase of the animation rect:
ScreenCanvas.Rectangle( r ); //draw frame
SysStuff.DelayMSecs( iDelay ); //
ScreenCanvas.Rectangle( r ); //pmXOR pen ...erase frame
Any ideas on why this is slow under Aero, or what I need to change?
procedure T_fmExplore.AnimateRects(ASourceRect, ADestRect: TRect; bExpand:
boolean; bAdjustSourceForFrame: boolean = True);
{ Draw animation frames in steps, for transition from ASourceRect to ADestRect.
bExpand: determines whether the animation rect is expanding or contracting.
bAdjustSourceForFrame: resize the animation rect smaller for the window frame. }
const
MINSTEPS = 10; //Min redraws of the animation rect (frame)
MAXSTEPS = 20; //30 was too many, too slow
MAXDELAY = 100; //Delay between drawing each rect frame
MINDELAY = 1;
var
iSteps: integer;
DeltaHt: Integer; //Rect size chg for each redraw of animation window
DeltaWidth: Integer;
DeltaTop : integer; //Origin change for each redraw
DeltaLeft : integer;
TgtWidth, TgtHt: Integer;
iTemp: Integer;
iDelay: integer;
r : TRect; //Animation frame's rect
ScreenCanvas: TCanvas;
begin
r := ASourceRect;
TgtWidth := ADestRect.Right - ADestRect.Left; //Target rect's Width
TgtHt := ADestRect.Bottom - ADestRect.Top; //Target rect's Height
//Initially Deltas hold total chg in Width & Height
DeltaWidth := TgtWidth - (r.Right - r.Left); //TgtWidth - old width
DeltaHt := TgtHt - (r.Bottom - r.Top);
//For smooth animation we adjust number of iSteps & Delay relative to the window area.
//Larger window = more iSteps and shorter Delay between drawing each step.
iSteps := Max( DeltaWidth * DeltaHt div 6500, MINSTEPS );
iSteps := Min( iSteps, MAXSTEPS );
//Now convert Deltas to the delta in window rect size
DeltaWidth := DeltaWidth div iSteps;
DeltaHt := DeltaHt div iSteps;
DeltaTop := (ADestRect.Top - ASourceRect.Top) div iSteps;
DeltaLeft := (ADestRect.Left - ASourceRect.Left) div iSteps;
iDelay := Max( MAXDELAY div iSteps, MINDELAY );
ScreenCanvas := TCanvas.Create;
try
ScreenCanvas.Handle := GetDC( 0 ); //Desktop
try
with ScreenCanvas do begin
Pen.Color := clWhite; //Do NOT use clBlack with pmXOR mode: with (r=0, g=0, b=0) there are no bytes to XOR.
Pen.Mode := pmXOR; //MUST use pmXOR pen, so 2nd Rectangle call (see below) erases what we drew.
Pen.Style := psSolid;
Pen.Width := 3; //Thin line. Was: Pen.Width := GetSystemMetrics(SM_CXFRAME);
Brush.Style := bsClear;
if bAdjustSourceForFrame then
InflateRect(ASourceRect, -Pen.Width, -Pen.Width);
repeat
iTemp := (r.Bottom - r.Top) + DeltaHt; //Height
if (bExpand and (iTemp > TgtHt)) or (not bExpand and (iTemp < TgtHt)) then begin
r.Top := ADestRect.Top;
r.Bottom := Top + TgtHt;
end else begin
r.Top := r.Top + DeltaTop; //Assign Top first...Bottom is calc'd from it
r.Bottom := r.Top + iTemp;
end;
iTemp := (r.Right - r.Left) + DeltaWidth; //Width
if (bExpand and (iTemp > TgtWidth)) or (not bExpand and (iTemp < TgtWidth)) then begin
r.Left := r.Left + DeltaLeft;
r.Right := r.Left + TgtWidth;
end else begin
r.Left := r.Left + DeltaLeft; //Assign Left first...Right is calc'd from it
r.Right := r.Left + iTemp;
end;
ScreenCanvas.Rectangle( r ); //draw frame
SysStuff.DelayMSecs( iDelay ); //
ScreenCanvas.Rectangle( r ); //pmXOR pen ...erase frame
until (r.Right - r.Left = TgtWidth) and (r.Bottom - r.Top = TgtHt);
end;
finally
ReleaseDC( 0, ScreenCanvas.Handle );
ScreenCanvas.Handle := 0;
end;
finally
ScreenCanvas.Free;
end;
end;

Visual select image part from image?

Greeting!
I have special image:
With which alghoritm I can select image part from this image & visually see what I was currentry selected?
Each image part delimited from others by 1 pixel by special color(In example is Fuchsia).
added:
Subimages(or any of them) may have any form.
Here is an example of finding subimages in your image.
This example is capable of finding any subimages with convex shape (rectangles, triangles, circles, etc). But it won't work correctly on concave shapes. For those you need to modify algorithm so that once you find first pixel you then go and scan for all nearbyones with similar algorithm as flod fill.
And here is the code:
unit Unit2;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ExtCtrls, Vcl.ComCtrls;
type
TSubImage = record
LeftBound: Integer;
RightBound: Integer;
TopBound: Integer;
BottomBound: Integer;
end;
ASubImages = Array of TSubImage;
TForm2 = class(TForm)
Button1: TButton;
SourceImage: TImage;
ListView1: TListView;
SelectionImage: TImage;
procedure Button1Click(Sender: TObject);
procedure ListView1SelectItem(Sender: TObject; Item: TListItem;
Selected: Boolean);
procedure FormCreate(Sender: TObject);
procedure SelectionImageMouseMove(Sender: TObject; Shift: TShiftState; X,
Y: Integer);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form2: TForm2;
SubImages: ASubImages;
implementation
{$R *.dfm}
procedure FindSubimages(Bitmap: TBitmap; var Subimages: ASubImages);
var X,Y,I: Integer;
//2D array we use to store to which image does which pixel belong
SubImagesMap: Array of Array of Integer;
begin
//Set the map dimension to the same dimension of TBitmap we scan
SetLength(SubImagesMap,Bitmap.Width+1,Bitmap.Height+1);
for Y := 0 to Bitmap.Height-1 do
begin
for X := 0 to Bitmap.Width-1 do
begin
//Check to see if current pixel color is not of background color.
if Bitmap.Canvas.Pixels[X,Y] <> clFuchsia then
begin
//Check if we already moved rightward (current pixel X postion > 0)
if X > 0 then
begin
//Check if pixel to the left has already been assigned to a subimage number
//and assign current pixel to the same subimage number since they are adjenct
if SubImagesMap[X-1,Y] <> 0 then
begin
SubImagesMap[X,Y] := SubImagesMap[X-1,Y];
//Here we are checking to see if current pixel is placed outside of subimage
//bonds and adjust them acordingly
//Check to se if pixel X position is leftwards to subimages left bound
if Subimages[SubImagesMap[X,Y]-1].LeftBound > X then
//Move subimage left bound to match pixel X position
Subimages[SubImagesMap[X,Y]-1].LeftBound := X;
//Check to se if pixel X position is rightwards to subimages right bound
if Subimages[SubImagesMap[X,Y]-1].RightBound < X then
//Move subimage right bound to match pixel X position
Subimages[SubImagesMap[X,Y]-1].RightBound := X;
//Check to se if pixel Y position is upwards to subimages top bound
if Subimages[SubImagesMap[X,Y]-1].TopBound > Y then
//Move subimage top bound to match pixel Y position
Subimages[SubImagesMap[X,Y]-1].TopBound := Y;
//Check to se if pixel Y position is downwards to subimages bottom bound
if Subimages[SubImagesMap[X,Y]-1].BottomBound < Y then
//Move subimage bottom bound to match pixel Y position
Subimages[SubImagesMap[X,Y]-1].BottomBound := Y;
end;
end;
//Check if we already moved downward (current pixel Y position > 0)
if Y > 0 then
begin
//Check if pixel above has already been assigned to a subimage number
//and assign current pixel to the same subimage number since they are adjenct
if SubImagesMap[X,Y-1] <> 0 then
begin
SubImagesMap[X,Y] := SubImagesMap[X,Y-1];
//Here we are checking to see if current pixel is placed outside of subimage
//bonds and adjust them acordingly
//Check to se if pixel X position is leftwards to subimages left bound
if Subimages[SubImagesMap[X,Y]-1].LeftBound > X then
//Move subimage left bound to match pixel X position
Subimages[SubImagesMap[X,Y]-1].LeftBound := X;
//Check to se if pixel X position is rightwards to subimages right bound
if Subimages[SubImagesMap[X,Y]-1].RightBound < X then
//Move subimage right bound to match pixel X position
Subimages[SubImagesMap[X,Y]-1].RightBound := X;
//Check to se if pixel Y position is upwards to subimages top bound
if Subimages[SubImagesMap[X,Y]-1].TopBound > Y then
//Move subimage top bound to match pixel Y position
Subimages[SubImagesMap[X,Y]-1].TopBound := Y;
//Check to se if pixel Y position is downwards to subimages bottom bound
if Subimages[SubImagesMap[X,Y]-1].BottomBound < Y then
//Move subimage bottom bound to match pixel Y position
Subimages[SubImagesMap[X,Y]-1].BottomBound := Y;
end;
end;
//Check to see if current pixel has already been asigned a sibimage number
//I not we create a new subimage entry and assign its number to current pixel
if SubImagesMap[X,Y] = 0 then
begin
//Increase the size of dynamic array storing subimage records
SetLength(SubImages,Length(SubImages)+1);
//Assing current pixel the number of newly created subimage
SubImagesMap[X,Y] := Length(SubImages);
//Set subimage initial bounds which are coordinates of one pixel
//since we created new subimage for this pixel
SubImages[SubImagesMap[X,Y]-1].LeftBound := X;
SubImages[SubImagesMap[X,Y]-1].RightBound := X;
SubImages[SubImagesMap[X,Y]-1].TopBound := Y;
SubImages[SubImagesMap[X,Y]-1].BottomBound := Y;
end;
end;
end;
end;
//Reduce the size of SubImageMap array to free its memory
//Since SubImageMap is local array this is optional
SetLength(SubImagesMap,0,0);
end;
procedure TForm2.Button1Click(Sender: TObject);
var I: Integer;
ListItem: TListItem;
ListColumn: TListColumn;
begin
//Our procedure for finding subimages. It accepts two parameters
//First parameter is reference to TBitmap object containing original image
//Second is reference to variable in which subimage bouns will be stored to
FindSubimages(SourceImage.Picture.Bitmap, Subimages);
//Lets show our results in more readable format
//First we change the ListView style to vsReport so we can show our results
//in multiple columns
ListView1.ViewStyle := vsReport;
//Then we add necessary columns
ListColumn := ListView1.Columns.Add;
ListColumn.Caption := 'Subimage number';
ListColumn.Width := 100;
ListColumn := ListView1.Columns.Add;
ListColumn.Caption := 'Left Bound';
ListColumn.Width := 80;
ListColumn := ListView1.Columns.Add;
ListColumn.Caption := 'Right Bound';
ListColumn.Width := 80;
ListColumn := ListView1.Columns.Add;
ListColumn.Caption := 'Top Bound';
ListColumn.Width := 80;
ListColumn := ListView1.Columns.Add;
ListColumn.Caption := 'Bottom Bound';
ListColumn.Width := 80;
//Iterate through all subimages and add data to ListView
for I := 0 to Length(Subimages)-1 do
begin
//Ad new item to list view
ListItem := ListView1.Items.Add;
//Use the reference of newly added item to set caption which will be the text
//in first column
ListItem.Caption := IntToStr(I+1);
//Add aditional subitems. Each of this subitems is shown in its own column
//NOTE: Make sure to have enough columns to show all subitems
//If you wanna field in certain column to be empty just pass an empty string ''
ListItem.SubItems.Add(IntToStr(SubImages[I].LeftBound));
ListItem.SubItems.Add(IntToStr(SubImages[I].RightBound));
ListItem.SubItems.Add(IntToStr(SubImages[I].TopBound));
ListItem.SubItems.Add(IntToStr(SubImages[I].BottomBound));
end;
end;
procedure TForm2.FormCreate(Sender: TObject);
begin
//Make selection image 2 pixels larger so we will never draw right to the edge
//and therefore can easily use its defult transparency
SelectionImage.Width := SourceImage.Width+2;
SelectionImage.Height := SourceImage.Height+2;
//Shift selector image position one to the left and one up to be centered above
//SourceIMage.
SelectionImage.Left := SourceImage.Left-1;
SelectionImage.Top := SourceImage.Top-1;
end;
procedure TForm2.ListView1SelectItem(Sender: TObject; Item: TListItem;
Selected: Boolean);
var Rect: TRect;
begin
//Use SubImage bounds to form rectagnle we will use for our selection
Rect.Left := SubImages[Item.Index].LeftBound+1;
Rect.Right := SubImages[Item.Index].RightBound+2;
Rect.Top := SubImages[Item.Index].TopBound+1;
Rect.Bottom := SubImages[Item.Index].BottomBound+2;
//Clear previous selection
SelectionImage.Canvas.Brush.Color := clFuchsia;
SelectionImage.Canvas.FillRect(SelectionImage.Canvas.ClipRect);
//Draw new selection rectangle
SelectionImage.Canvas.Brush.Color := clLime;
SelectionImage.Canvas.FrameRect(Rect);
end;
procedure TForm2.SelectionImageMouseMove(Sender: TObject; Shift: TShiftState; X,
Y: Integer);
begin
Form2.Caption := IntToStr(X);
end;
end.
As I see it, you have a rectangular sea of fuchsia which contains a number of non-fuchsia islands. You want to identify those islands. A simple algorithm is as follows:
Start at one corner of the image, say the top left.
Work through the image pixel by pixel, row by row. So, process the first row left to right, then the next row, and so on.
When you find a non-fuchsia pixel, that's the top left corner of an island. Now find the rest of the island. Continue along the top row of the island until you reach the end of the row, or find a fuchsia pixel. Now you know the width. Find the height by moving down one of the columns until you reach the bottom row, or find a fuchsia pixel.
Now you know the top left coordinate, and the width and height, for that island. Capture what you need with that information and replace the island rectangle in the source image with fuchsia to indicate that those pixels are all dealt with.
Continue from the top right of the island you just captured looking for the next island.
When you reach the bottom right of the image you have identified all the islands.

Snapping vertical line cursor to data point in jqPlot

I have just started using jqPlot for a line chart with multiple series. It seems great.
I have added the Cursor plugin with the intention of showing a vertical line on the nearest data point on the x axis. In other words, it snaps to the nearest point. The Cursor plugin, however always shows the vertical cursor right where the mouse is.
It seems like I just want to "override" or replace moveLine to change the current functionality.
What's the most appropriate way of doing so?
It seems a little much to copy/past all of the cursor plugin just to modify a very small subset.
Thanks!
I know I'm a kind of archaeologist by edited this post but I think the following can be useful for someone (I hope).
I've made a piece of code which allow to draw a vertical line following the cursor and displaying a tooltip on the nearest point (according to the cursor). You can find a demo of it on this JSFiddle.
I also post the corresponding code below (only the part which calculate nearest point and display tooltip):
//Show nearest point's tooltip
$("#chart1").bind('jqplotMouseMove', function(ev, gridpos, datapos, neighbor, data){
var c_x = datapos.xaxis;
var index_x = -1;
var pos_index = 0;
var low = 0;
var high = data.data[0].length-1;
while(high - low > 1){
var mid = Math.round((low+high)/2);
var current = data.data[0][mid][0];
if(current <= c_x)
low = mid;
else
high = mid;
}
if(data.data[0][low][0] == c_x){
high = low;
index_x = high;
}else{
var c_low = data.data[0][low][0];
var c_high = data.data[0][high][0];
if(Math.abs(c_low - c_x) < Math.abs(c_high - c_x)){
index_x = low;
}else{
index_x = high;
}
}
//Display marker and tooltip
if(data.series[0].data[index_x]){
var x = data.series[0].gridData[index_x][0];
var y = data.series[0].gridData[index_x][1];
var r = 5;
var highlightCanvas = $(".jqplot-highlight-canvas")[0];
var context = highlightCanvas.getContext('2d');
context.clearRect(0,0,highlightCanvas.width,highlightCanvas.height);
context.strokeStyle = 'rgba(47,164,255,1)';
context.fillStyle = 'rgba(47,164,255,1)';
context.beginPath();
context.arc(x,y,r,0,Math.PI*2,true);
context.closePath();
context.stroke();
context.fill();
//Display tooltip on nearest point
var highlightTooltip = $(".jqplot-highlighter-tooltip");
var val_x = data.data[0][index_x][0];
var val_y = data.data[0][index_x][1];
highlightTooltip.html("X : "+val_x+"<br/>Y : "+val_y);
highlightTooltip.css({'left': x+'px', 'top': (y-10)+'px', 'display': 'block'});
}
});
Feel please to use it and to modify it as you need it.
Try a bar graph series on top of everything else that has alpha set to 0.

Resources