Mercurial > dive4elements > river
comparison flys-artifacts/src/main/java/de/intevation/flys/jfree/StableXYDifferenceRenderer.java @ 1884:4ae9c92feb8c
StableXYDifferenceRenderer: Make rendering work with definition holes.
flys-artifacts/trunk@3243 c6561f87-3c4e-4783-a992-168aeb5c3f6f
author | Sascha L. Teichmann <sascha.teichmann@intevation.de> |
---|---|
date | Sun, 13 Nov 2011 14:02:35 +0000 |
parents | eb671699fbc2 |
children | 110dd067bb8f |
comparison
equal
deleted
inserted
replaced
1883:eb671699fbc2 | 1884:4ae9c92feb8c |
---|---|
114 import org.jfree.chart.renderer.xy.XYItemRenderer; | 114 import org.jfree.chart.renderer.xy.XYItemRenderer; |
115 import org.jfree.chart.renderer.xy.XYItemRendererState; | 115 import org.jfree.chart.renderer.xy.XYItemRendererState; |
116 | 116 |
117 import gnu.trove.TDoubleArrayList; | 117 import gnu.trove.TDoubleArrayList; |
118 | 118 |
119 import de.intevation.flys.artifacts.math.Linear; | |
120 | |
119 /** | 121 /** |
120 * A renderer for an {@link XYPlot} that highlights the differences between two | 122 * A renderer for an {@link XYPlot} that highlights the differences between two |
121 * series. The example shown here is generated by the | 123 * series. The example shown here is generated by the |
122 * <code>DifferenceChartDemo1.java</code> program included in the JFreeChart | 124 * <code>DifferenceChartDemo1.java</code> program included in the JFreeChart |
123 * demo collection: | 125 * demo collection: |
362 */ | 364 */ |
363 public int getPassCount() { | 365 public int getPassCount() { |
364 return 2; | 366 return 2; |
365 } | 367 } |
366 | 368 |
367 public static List<XYDataset> splitByNaNs(XYDataset dataset) { | 369 private static final void addSeries( |
370 DefaultXYDataset ds, | |
371 Comparable key, | |
372 TDoubleArrayList xs, | |
373 TDoubleArrayList ys | |
374 ) { | |
375 ds.addSeries( | |
376 key, | |
377 new double [][] { | |
378 xs.toNativeArray(), | |
379 ys.toNativeArray() | |
380 }); | |
381 } | |
382 | |
383 protected static List<XYDataset> splitByNaNsOneSeries( | |
384 XYDataset dataset | |
385 ) { | |
368 List<XYDataset> datasets = new ArrayList<XYDataset>(); | 386 List<XYDataset> datasets = new ArrayList<XYDataset>(); |
369 | 387 |
370 switch (dataset.getSeriesCount()) { | 388 int N = dataset.getItemCount(0); |
371 case 0: | 389 TDoubleArrayList xs = new TDoubleArrayList(N); |
372 return datasets; | 390 TDoubleArrayList ys = new TDoubleArrayList(N); |
373 case 1: | 391 for (int i = 0; i < N; ++i) { |
374 int N = dataset.getItemCount(0); | 392 double x = dataset.getXValue(0, i); |
375 TDoubleArrayList xs = new TDoubleArrayList(N); | 393 double y = dataset.getYValue(0, i); |
376 TDoubleArrayList ys = new TDoubleArrayList(N); | 394 if (Double.isNaN(x) || Double.isNaN(y)) { |
377 for (int i = 0; i < N; ++i) { | 395 if (!xs.isEmpty()) { |
396 DefaultXYDataset ds = new DefaultXYDataset(); | |
397 addSeries(ds, dataset.getSeriesKey(0), xs, ys); | |
398 datasets.add(ds); | |
399 xs.reset(); | |
400 ys.reset(); | |
401 } | |
402 } | |
403 else { | |
404 xs.add(x); | |
405 ys.add(y); | |
406 } | |
407 } | |
408 if (!xs.isEmpty()) { | |
409 DefaultXYDataset ds = new DefaultXYDataset(); | |
410 addSeries(ds, dataset.getSeriesKey(0), xs, ys); | |
411 datasets.add(ds); | |
412 } | |
413 | |
414 return datasets; | |
415 } | |
416 | |
417 private static final boolean add(TDoubleArrayList xs, double x) { | |
418 int N = xs.size(); | |
419 if (N == 0 || xs.getQuick(N-1) < x) { | |
420 xs.add(x); | |
421 return true; | |
422 } | |
423 System.err.println("pushed smaller"); | |
424 return false; | |
425 } | |
426 | |
427 protected static List<XYDataset> splitByNaNsTwoSeries( | |
428 XYDataset dataset | |
429 ) { | |
430 List<XYDataset> datasets = new ArrayList<XYDataset>(); | |
431 | |
432 int N = dataset.getItemCount(0); | |
433 int M = dataset.getItemCount(1); | |
434 | |
435 int i = 0, j = 0; | |
436 // ignore leading NaNs | |
437 for (; i < N; ++i) { | |
438 double x = dataset.getXValue(0, i); | |
439 double y = dataset.getYValue(0, i); | |
440 if (!Double.isNaN(x) && !Double.isNaN(y)) { | |
441 break; | |
442 } | |
443 } | |
444 | |
445 for (; j < M; ++j) { | |
446 double x = dataset.getXValue(1, j); | |
447 double y = dataset.getYValue(1, j); | |
448 if (!Double.isNaN(x) && !Double.isNaN(y)) { | |
449 break; | |
450 } | |
451 } | |
452 | |
453 TDoubleArrayList six = new TDoubleArrayList(); | |
454 TDoubleArrayList siy = new TDoubleArrayList(); | |
455 TDoubleArrayList sjx = new TDoubleArrayList(); | |
456 TDoubleArrayList sjy = new TDoubleArrayList(); | |
457 | |
458 while (i < N && j < M) { | |
459 int ni = i+1; | |
460 for (; ni < N && !Double.isNaN(dataset.getXValue(0, ni)); ++ni); | |
461 for (; ni < N && Double.isNaN(dataset.getXValue(0, ni)); ++ni); | |
462 | |
463 int nj = j+1; | |
464 for (; nj < M && !Double.isNaN(dataset.getXValue(1, nj)); ++nj); | |
465 for (; nj < M && Double.isNaN(dataset.getXValue(1, nj)); ++nj); | |
466 | |
467 if (ni == N && nj == M) { // no more splits | |
468 System.err.println("no more splits ...."); | |
469 for (; i < ni; ++i) { | |
378 double x = dataset.getXValue(0, i); | 470 double x = dataset.getXValue(0, i); |
379 double y = dataset.getYValue(0, i); | 471 double y = dataset.getYValue(0, i); |
380 if (Double.isNaN(x) || Double.isNaN(y)) { | 472 if (!Double.isNaN(x) |
381 if (!xs.isEmpty()) { | 473 && !Double.isNaN(y) |
382 DefaultXYDataset ds = new DefaultXYDataset(); | 474 && add(six, x)) { |
383 datasets.add(ds); | 475 siy.add(y); |
384 ds.addSeries( | 476 } |
385 dataset.getSeriesKey(0), | 477 } |
386 new double [][] { | 478 for (; j < nj; ++j) { |
387 xs.toNativeArray(), | 479 double x = dataset.getXValue(1, j); |
388 ys.toNativeArray() }); | 480 double y = dataset.getYValue(1, j); |
389 xs.reset(); | 481 if (!Double.isNaN(x) |
390 ys.reset(); | 482 && !Double.isNaN(y) |
483 && add(sjx, x)) { | |
484 sjy.add(y); | |
485 } | |
486 } | |
487 if (!six.isEmpty() && !sjx.isEmpty()) { | |
488 DefaultXYDataset ds = new DefaultXYDataset(); | |
489 addSeries(ds, dataset.getSeriesKey(0), six, siy); | |
490 addSeries(ds, dataset.getSeriesKey(1), sjx, sjy); | |
491 datasets.add(ds); | |
492 } | |
493 break; | |
494 } | |
495 | |
496 System.err.println("ni: " + ni + " " + N); | |
497 System.err.println("nj: " + nj + " " + M); | |
498 | |
499 double xni = ni < N | |
500 ? dataset.getXValue(0, ni) | |
501 : Double.MAX_VALUE; | |
502 | |
503 double xnj = nj < M | |
504 ? dataset.getXValue(1, nj) | |
505 : Double.MAX_VALUE; | |
506 | |
507 double xns = Math.min(xni, xnj); | |
508 | |
509 double pushxi = Double.NaN; | |
510 double pushyi = Double.NaN; | |
511 double pushxj = Double.NaN; | |
512 double pushyj = Double.NaN; | |
513 | |
514 for (; i < ni; ++i) { | |
515 double x = dataset.getXValue(0, i); | |
516 double y = dataset.getYValue(0, i); | |
517 if (Double.isNaN(x) || Double.isNaN(y)) { | |
518 continue; | |
519 } | |
520 if (x < xns) { | |
521 if (add(six, x)) { | |
522 siy.add(y); | |
523 } | |
524 continue; | |
525 } | |
526 if (x == xns) { // exact match | |
527 if (add(six, x)) { | |
528 siy.add(y); | |
529 } | |
530 pushxi = x; pushyi = y; | |
531 } | |
532 else { // x > xns: intersection | |
533 System.err.println("xns: " + xns); | |
534 System.err.println("x/y: " + x + " / " + y); | |
535 int SIX = six.size(); | |
536 if (SIX > 0) { // should always be true | |
537 double yns = Linear.linear( | |
538 xns, | |
539 six.getQuick(SIX-1), x, | |
540 siy.getQuick(SIX-1), y); | |
541 System.err.println("intersection at: " + yns); | |
542 if (add(six, xns)) { | |
543 siy.add(yns); | |
391 } | 544 } |
545 pushxi = xns; | |
546 pushyi = yns; | |
392 } | 547 } |
393 else { | 548 } |
394 xs.add(x); | 549 break; // Split point reached. |
395 ys.add(y); | 550 } |
551 | |
552 for (; j < nj; ++j) { | |
553 double x = dataset.getXValue(1, j); | |
554 double y = dataset.getYValue(1, j); | |
555 if (Double.isNaN(x) || Double.isNaN(y)) { | |
556 continue; | |
557 } | |
558 if (x < xns) { | |
559 if (add(sjx, x)) { | |
560 sjy.add(y); | |
396 } | 561 } |
397 } | 562 continue; |
398 if (!xs.isEmpty()) { | 563 } |
399 DefaultXYDataset ds = new DefaultXYDataset(); | 564 if (x == xns) { // exact match |
400 datasets.add(ds); | 565 if (add(sjx, x)) { |
401 ds.addSeries( | 566 sjy.add(y); |
402 dataset.getSeriesKey(0), | 567 } |
403 new double [][] { | 568 pushxj = x; pushyj = y; |
404 xs.toNativeArray(), | 569 } |
405 ys.toNativeArray() }); | 570 else { // x > xns: intersection |
406 } | 571 int SJX = sjx.size(); |
407 break; | 572 if (SJX > 0) { // should always be true |
573 double yns = Linear.linear( | |
574 xns, | |
575 sjx.getQuick(SJX-1), x, | |
576 sjy.getQuick(SJX-1), y); | |
577 System.err.println("intersection at: " + yns); | |
578 if (add(sjx, xns)) { | |
579 sjy.add(yns); | |
580 } | |
581 pushxj = xns; pushyj = yns; | |
582 } | |
583 } | |
584 break; // Split point reached. | |
585 } | |
586 | |
587 if (!six.isEmpty() && !sjx.isEmpty()) { | |
588 DefaultXYDataset ds = new DefaultXYDataset(); | |
589 addSeries(ds, dataset.getSeriesKey(0), six, siy); | |
590 addSeries(ds, dataset.getSeriesKey(1), sjx, sjy); | |
591 datasets.add(ds); | |
592 } | |
593 | |
594 six.reset(); siy.reset(); | |
595 sjx.reset(); sjy.reset(); | |
596 | |
597 // Push split points. | |
598 if (!Double.isNaN(pushxi)) { | |
599 six.add(pushxi); | |
600 siy.add(pushyi); | |
601 } | |
602 | |
603 if (!Double.isNaN(pushxj)) { | |
604 sjx.add(pushxj); | |
605 sjy.add(pushyj); | |
606 } | |
607 } | |
608 | |
609 // Copy the rest. | |
610 for (; i < N; ++i) { | |
611 double x = dataset.getXValue(0, i); | |
612 double y = dataset.getXValue(0, i); | |
613 if (!Double.isNaN(x) | |
614 && !Double.isNaN(y) | |
615 && add(six, x)) { | |
616 siy.add(y); | |
617 } | |
618 } | |
619 | |
620 for (; j < M; ++j) { | |
621 double x = dataset.getXValue(1, j); | |
622 double y = dataset.getXValue(1, j); | |
623 if (!Double.isNaN(x) | |
624 && !Double.isNaN(y) | |
625 && add(sjx, x)) { | |
626 sjy.add(y); | |
627 } | |
628 } | |
629 | |
630 // Build final dataset. | |
631 if (!six.isEmpty() && !sjx.isEmpty()) { | |
632 DefaultXYDataset ds = new DefaultXYDataset(); | |
633 addSeries(ds, dataset.getSeriesKey(0), six, siy); | |
634 addSeries(ds, dataset.getSeriesKey(1), sjx, sjy); | |
635 datasets.add(ds); | |
636 } | |
637 | |
638 System.err.println("datasets after split: " + datasets.size()); | |
639 | |
640 return datasets; | |
641 } | |
642 | |
643 public static List<XYDataset> splitByNaNs(XYDataset dataset) { | |
644 | |
645 switch (dataset.getSeriesCount()) { | |
646 case 0: | |
647 return Collections.emptyList(); | |
648 case 1: | |
649 return splitByNaNsOneSeries(dataset); | |
408 default: // two or more | 650 default: // two or more |
409 // TODO: split two parts | 651 return splitByNaNsTwoSeries(dataset); |
410 datasets.add(dataset); | 652 } |
411 break; | |
412 } | |
413 | |
414 return datasets; | |
415 } | 653 } |
416 | 654 |
417 /** | 655 /** |
418 * Draws the visual representation of a single data item. | 656 * Draws the visual representation of a single data item. |
419 * | 657 * |
447 switch (pass) { | 685 switch (pass) { |
448 case 0: | 686 case 0: |
449 for (XYDataset ds: splitByNaNs(dataset)) { | 687 for (XYDataset ds: splitByNaNs(dataset)) { |
450 drawItemPass0(g2, dataArea, info, | 688 drawItemPass0(g2, dataArea, info, |
451 plot, domainAxis, rangeAxis, | 689 plot, domainAxis, rangeAxis, |
452 dataset, series, item, crosshairState); | 690 ds, series, item, crosshairState); |
453 } | 691 } |
454 break; | 692 break; |
455 case 1: | 693 case 1: |
456 drawItemPass1(g2, dataArea, info, | 694 drawItemPass1(g2, dataArea, info, |
457 plot, domainAxis, rangeAxis, | 695 plot, domainAxis, rangeAxis, |