getNearestDatumDetailPerSeries method

  1. @override
List<DatumDetails<D>> getNearestDatumDetailPerSeries(
  1. Point<double> chartPoint,
  2. bool byDomain,
  3. Rectangle<int>? boundsOverride, {
  4. bool selectOverlappingPoints = false,
  5. bool selectExactEventLocation = false,
})
override

Gets a list of the data from each series that is closest to a given point.

chartPoint represents a point in the chart, such as a point that was clicked/tapped on by a user.

selectOverlappingPoints specifies whether to include all points that overlap the tapped position in the result. If specified, the method will return either the closest point or all the overlapping points with the tapped position.

byDomain specifies whether the nearest data should be defined by domain distance, or relative Cartesian distance.

boundsOverride optionally specifies a bounding box for the selection event. If specified, then no data should be returned if chartPoint lies outside the box. If not specified, then each series renderer on the chart will use its own component bounds for filtering out selection events (usually the chart draw area).

Implementation

@override
List<DatumDetails<D>> getNearestDatumDetailPerSeries(
  Point<double> chartPoint,
  bool byDomain,
  Rectangle<int>? boundsOverride, {
  bool selectOverlappingPoints = false,
  bool selectExactEventLocation = false,
}) {
  final nearest = <DatumDetails<D>>[];

  // Was it even in the component bounds?
  if (!isPointWithinBounds(chartPoint, boundsOverride)) {
    return nearest;
  }

  var isFirstSeriesAbovePoint = false;

  for (final seriesSegments in _seriesLineMap.values) {
    _DatumPoint<D>? nearestPoint;
    var nearestDomainDistance = 10000.0;
    var nearestMeasureDistance = 10000.0;
    var nearestRelativeDistance = 10000.0;

    for (final segment in seriesSegments) {
      if (segment.overlaySeries) {
        continue;
      }

      for (final p in segment.allPoints) {
        // Don't look at points not in the drawArea.
        if (p.x! < componentBounds!.left || p.x! > componentBounds!.right) {
          continue;
        }

        double measureDistance;
        double relativeDistance;
        double domainDistance;

        if (p.y != null) {
          measureDistance = (p.y! - chartPoint.y).abs();
          domainDistance = (p.x! - chartPoint.x).abs();
          relativeDistance = chartPoint.distanceTo(p.toPoint());
        } else {
          // Null measures have no real position, so make them the farthest
          // away by real distance.
          measureDistance = double.infinity;
          domainDistance = double.infinity;
          relativeDistance = byDomain ? domainDistance : double.infinity;
        }

        if (byDomain) {
          if ((domainDistance < nearestDomainDistance) ||
              ((domainDistance == nearestDomainDistance) &&
                  (measureDistance < nearestMeasureDistance))) {
            nearestPoint = p;
            nearestDomainDistance = domainDistance;
            nearestMeasureDistance = measureDistance;
            nearestRelativeDistance = relativeDistance;
          }
        } else {
          if (relativeDistance < nearestRelativeDistance) {
            nearestPoint = p;
            nearestDomainDistance = domainDistance;
            nearestMeasureDistance = measureDistance;
            nearestRelativeDistance = relativeDistance;
          }
        }
      }

      // For area charts, check if the current series is the first series
      // above [chartPoint] or not. If it is, it means that [chartPoint] is
      // inside the area skirt of the current series. In this case, set the
      // measure distance to 0 so the current [nearestPoint] has the smallest
      // measure distance among all.
      if (config.includeArea &&
          !isFirstSeriesAbovePoint &&
          nearestPoint != null &&
          _isPointBelowSeries(chartPoint, nearestPoint, segment.allPoints)) {
        nearestMeasureDistance = 0;
        isFirstSeriesAbovePoint = true;
      }
    }

    // Found a point, add it to the list.
    if (nearestPoint != null) {
      nearest.add(DatumDetails<D>(
          chartPosition: NullablePoint(nearestPoint.x, nearestPoint.y),
          datum: nearestPoint.datum,
          domain: nearestPoint.domain,
          series: nearestPoint.series,
          domainDistance: nearestDomainDistance,
          measureDistance: nearestMeasureDistance,
          relativeDistance: nearestRelativeDistance));
    }
  }

  // Note: the details are already sorted by domain & measure distance in
  // base chart.

  return nearest;
}