diff --git a/pom.xml b/pom.xml index 68457b1..7a70499 100644 --- a/pom.xml +++ b/pom.xml @@ -32,7 +32,7 @@ 1.8 1.8 UTF-8 - 2.11.7 + 2.12.2 diff --git a/src/main/scala/org/sameersingh/scalaplot/Label.scala b/src/main/scala/org/sameersingh/scalaplot/Label.scala new file mode 100644 index 0000000..24e3f37 --- /dev/null +++ b/src/main/scala/org/sameersingh/scalaplot/Label.scala @@ -0,0 +1,10 @@ +package org.sameersingh.scalaplot + +/** + * Label class - consists of text and coordinates + * + * Created by vpatryshev on 6/5/17. + */ +case class Label(text: String, x: Double, y: Double) { + def gpl = s"$text,$x,$y" +} diff --git a/src/main/scala/org/sameersingh/scalaplot/LabelChart.scala b/src/main/scala/org/sameersingh/scalaplot/LabelChart.scala new file mode 100644 index 0000000..33e716a --- /dev/null +++ b/src/main/scala/org/sameersingh/scalaplot/LabelChart.scala @@ -0,0 +1,13 @@ +package org.sameersingh.scalaplot + +/** + * A chart consisting of a cloud of labels + * Created by vpatryshev on 6/5/17. + */ +class LabelChart(chartTitle: Option[String], val data: Iterable[Label], + val x: NumericAxis = new NumericAxis, + val y: NumericAxis = new NumericAxis) extends Chart { + + def this(title: String, data: Iterable[Label]) = this(Some(title), data) +} + diff --git a/src/main/scala/org/sameersingh/scalaplot/gnuplot/GnuplotPlotter.scala b/src/main/scala/org/sameersingh/scalaplot/gnuplot/GnuplotPlotter.scala index c807589..c9e9bce 100644 --- a/src/main/scala/org/sameersingh/scalaplot/gnuplot/GnuplotPlotter.scala +++ b/src/main/scala/org/sameersingh/scalaplot/gnuplot/GnuplotPlotter.scala @@ -273,13 +273,29 @@ class GnuplotPlotter(chart: Chart) extends Plotter(chart) { def postPlotFileXYSeries(series: FileXYSeries) {} + def plotLabelChart(chart: LabelChart) = { + lines += + """# LabelChart settings + |set termoption enhanced + |set encoding utf8 + |unset xtics + |unset ytics + |unset key + |set border 0 + |set size square + |set datafile separator "," + |plot '-' using 2:3:1 with labels + """.stripMargin + + for (label <- chart.data) lines += label.gpl + } + def writeScriptFile(directory: String, filenamePrefix: String, terminal: String, filenameSuffix: String, stdout: Boolean = false, defaultTerminal: String = "dumb") { // write the description - assert(new File(directory).isDirectory, directory + " should be a directory") - assert(directory.endsWith("/"), directory + " should end with a /") + assert(new File(directory).isDirectory, s"$directory should be a directory - current is ${new File(".").getCanonicalPath}") reset - this.directory = directory + this.directory = if (directory.endsWith("/")) directory else (directory+"/") filename = filenamePrefix plotChart(chart, terminal) lines += "set terminal %s" format (terminal) @@ -288,13 +304,14 @@ class GnuplotPlotter(chart: Chart) extends Plotter(chart) { chart match { case xyc: XYChart => plotXYChart(xyc) case bc: BarChart => plotBarChart(bc) + case lc: LabelChart => plotLabelChart(lc) } lines += "unset output" lines += "# Wrapup" lines += "set terminal %s" format (defaultTerminal) lines += "refresh" - val scriptFile = directory + filenamePrefix + ".gpl" + val scriptFile = this.directory + filenamePrefix + ".gpl" val writer = new PrintWriter(scriptFile) for (line <- lines) { writer.println(line) @@ -321,7 +338,7 @@ class GnuplotPlotter(chart: Chart) extends Plotter(chart) { override def svg(directory: String, filenamePrefix: String): String = { if (chart.monochrome) println("Warning: Monochrome ignored.") val sizeString = if (chart.size.isDefined) "size %f,%f" format(chart.size.get._1, chart.size.get._2) else "" - val terminal = "svg enhanced linewidth 3.0 %s" format (sizeString) + val terminal = "svg enhanced linewidth 3.0 %s" format sizeString writeScriptFile(directory, filenamePrefix, terminal, "svg", true, "unknown") runGnuplot(directory, filenamePrefix) } @@ -346,7 +363,7 @@ class GnuplotPlotter(chart: Chart) extends Plotter(chart) { val terminal = "canvas enhanced name\"%s\"" format (filenamePrefix) writeScriptFile(directory, filenamePrefix, terminal, "js") runGnuplot(directory, filenamePrefix) - htmlWrap(directory, filenamePrefix) + htmlWrap(filenamePrefix) } def js2(directory: String, filenamePrefix: String): String = { @@ -372,10 +389,10 @@ class GnuplotPlotter(chart: Chart) extends Plotter(chart) { } writer.close() val str = runGnuplot(directory, filenamePrefix) - htmlWrap(directory, filenamePrefix) + htmlWrap(filenamePrefix) } - private def htmlWrap(directory: String, filenamePrefix: String, jsDir: String = "/usr/local/Cellar/gnuplot/4.6.5/share/gnuplot/4.6/js") = { + private def htmlWrap(filenamePrefix: String, jsDir: String = "/usr/local/Cellar/gnuplot/4.6.5/share/gnuplot/4.6/js") = { """ | | @@ -399,12 +416,10 @@ class GnuplotPlotter(chart: Chart) extends Plotter(chart) { val cmdLine = "gnuplot " + filenamePrefix + ".gpl" try { - val p = Runtime.getRuntime().exec(cmdLine, Array.empty[String], new File(directory)) - val input = new BufferedReader(new InputStreamReader(p.getInputStream())) - while (({ - line = input.readLine(); - line - }) != null) { + println(s"${new File(directory).getCanonicalPath}> $cmdLine") + val p = Runtime.getRuntime.exec(cmdLine, Array.empty[String], new File(directory)) + val input = new BufferedReader(new InputStreamReader(p.getInputStream)) + while (input.readLine() != null) { output += (line + '\n') } input.close() diff --git a/src/test/resources/cities.dat b/src/test/resources/cities.dat new file mode 100644 index 0000000..f56d2c5 --- /dev/null +++ b/src/test/resources/cities.dat @@ -0,0 +1,104 @@ +Paris France 2110420 48.86° 2.34° +Marseille France 820729 43.31° 5.37° +Lyon France 443859 45.76° 4.83° +Toulouse France 411768 43.62° 1.45° +Nice France 331958 43.70° 7.27° +Nantes France 282251 47.23° -1.57° +Strasbourg France 272599 48.58° 7.76° +Montpellier France 230663 43.61° 3.87° +Bordeaux France 216978 44.84° -0.58° +Rennes France 212616 48.11° -1.68° +Le Havre France 189329 49.50° 0.12° +Reims France 184594 49.25° 4.03° +Lille France 180547 50.64° 3.07° +"Saint-Étienne" France 170734 45.43° 4.39° +Toulon France 161160 43.13° 5.92° +Grenoble France 155621 45.19° 5.72° +Angers France 153340 47.48° -0.54° +Brest France 151526 48.39° -4.50° +Le Mans France 148654 48.00° 0.20° +Dijon France 147153 47.33° 5.03° +Aix-en-Provence France 138060 43.53° 5.44° +Clermont-Ferrand France 137274 45.78° 3.08° +"Nîmes" France 136424 43.84° 4.35° +Amiens France 135768 49.90° 2.30° +Tours France 133538 47.38° 0.69° +Limoges France 132299 45.83° 1.25° +Metz France 125010 49.12° 6.18° +Villeurbanne France 121852 45.77° 4.88° +"Besançon" France 118152 47.24° 6.02° +Caen France 116259 49.19° -0.36° +"Orléans" France 112327 47.90° 1.90° +Mulhouse France 111307 47.76° 7.34° +Perpignan France 110149 42.70° 2.89° +Boulogne-Billancourt France 108079 48.83° 2.24° +Rouen France 105310 49.44° 1.08° +Nancy France 103196 48.69° 6.17° +Avignon France 86865 43.96° 4.81° +Nanterre France 85753 48.90° 2.20° +Poitiers France 85406 46.58° 0.34° +Versailles France 82101 48.81° 2.14° +"Créteil" France 80168 48.79° 2.45° +Pau France 79449 43.30° -0.39° +La Rochelle France 78854 46.17° -1.18° +Bourges France 70100 47.08° 2.39° +Colmar France 65695 48.08° 7.36° +Valence France 65016 44.94° 4.89° +Quimper France 64038 47.99° -4.11° +Troyes France 60353 48.30° 4.08° +"Chambéry" France 56022 45.58° 5.91° +Niort France 55868 46.32° -0.47° +"Charleville-Mézières" France 55343 49.75° 4.73° +Beauvais France 54116 49.43° 2.09° +Vannes France 52436 47.66° -2.76° +Montauban France 51839 44.02° 1.35° +Laval France 51504 48.07° -0.78° +"Évreux" France 50882 49.03° 1.14° +La Roche-sur-Yon France 50608 46.67° -1.44° +Belfort France 50109 47.65° 6.85° +Annecy France 49903 45.91° 6.12° +Blois France 49207 47.59° 1.32° +"Châteauroux" France 48724 46.81° 1.70° +Ajaccio France 47914 41.93° 8.73° +"Évry" France 46928 48.64° 2.44° +"Châlons-en-Champagne" France 46678 48.97° 4.36° +Albi France 46540 43.93° 2.15° +Tarbes France 46048 43.24° 0.08° +Carcassonne France 44311 43.22° 2.34° +Saint-Brieuc France 44081 48.52° -2.77° +"Angoulême" France 42995 45.66° 0.15° +Bobigny France 42506 48.91° 2.44° +Bourg-en-Bresse France 41174 46.21° 5.21° +Bastia France 41160 42.71° 9.46° +Arras France 40529 50.29° 2.78° +Nevers France 39648 47.00° 3.15° +Chartres France 39114 48.46° 1.48° +Auxerre France 37581 47.81° 3.56° +Gap France 36131 44.57° 6.08° +Melun France 35044 48.54° 2.65° +"Épinal" France 35040 48.18° 6.44° +"Mâcon" France 33624 46.31° 4.81° +Mont-de-Marsan France 30684 43.90° -0.51° +Agen France 30554 44.20° 0.62° +"Périgueux" France 29980 45.18° 0.71° +Aurillac France 29402 44.92° 2.43° +"Alençon" France 28628 48.44° 0.09° +Pontoise France 26610 49.05° 2.09° +Laon France 25925 49.58° 3.63° +Chaumont France 25086 48.11° 5.14° +Rodez France 23407 44.36° 2.57° +Auch France 21962 43.65° 0.58° +Moulins France 21241 46.57° 3.33° +Le Puy-en-Velay France 20783 45.05° 3.88° +Cahors France 20645 44.46° 1.44° +"Saint-Lô" France 19703 49.13° -1.11° +Lons-le-Saunier France 18169 46.68° 5.53° +Vesoul France 17128 47.63° 6.15° +Bar-le-Duc France 16755 48.78° 5.17° +Digne-les-Bains France 15941 44.10° 6.23° +Tulle France 15206 45.26° 1.76° +"Guéret" France 13858 46.17° 1.87° +Mende France 11915 44.52° 3.48° +Privas France 9165 44.73° 4.58° +Foix France 9135 42.97° 1.60° + diff --git a/src/test/scala/org/sameersingh/scalaplot/ExampleLabelTest.scala b/src/test/scala/org/sameersingh/scalaplot/ExampleLabelTest.scala new file mode 100644 index 0000000..e336b97 --- /dev/null +++ b/src/test/scala/org/sameersingh/scalaplot/ExampleLabelTest.scala @@ -0,0 +1,69 @@ +package org.sameersingh.scalaplot + +import java.io.File + +import org.junit._ +import org.sameersingh.scalaplot.Implicits.{output, xyChart} +import org.sameersingh.scalaplot.Style._ +import org.sameersingh.scalaplot.gnuplot.GnuplotPlotter + +import scala.io.Source +import scala.language.postfixOps + +/** + * Examples that demonstrate how to use the library. Not really tests. + * @author sameer + */ +@Test +class ExampleLabelTest { + + @Test + def testLabelsSimple(): Unit = { + val cities: Iterable[Label] = readCities + + val chart = new LabelChart("France", cities) + + val folder = java.io.File.createTempFile("labelsample", "pdf") + folder.delete() + folder.mkdir() + val folderpath = folder.getCanonicalPath + println(folderpath) + + val gpl = new GnuplotPlotter(chart) + println(gpl.string(folderpath, "plot_string")) + gpl.js(folderpath, "plot_js") + gpl.svg(folderpath, "plot_svg") + gpl.html(folderpath, "plot_html") + gpl.pdf(folderpath, "plot_pdf") + gpl.png(folderpath, "plot_png") + +/* + + */ + } + + @Test + def testLabelsPNG(): Unit = { + import org.sameersingh.scalaplot.Implicits._ + + val cities: Iterable[Label] = readCities + + val chart = new LabelChart("France", cities) + + val folder = java.io.File.createTempFile("labelsample", "pdf") + folder.delete() + folder.mkdir() + val folderpath = folder.getCanonicalPath + println(folderpath) + + println(output(PNG(folder.getPath, "cities"), chart)) + } + + private def readCities: Iterable[Label] = { + val Format = "(.+)\\t(\\w+)\\t(\\w+)\\t([^°]+)°\\t([^°]+)°".r + + Source.fromResource("cities.dat").getLines collect { + case Format(name, _, size, lat, lon) => Label(name, lat.toDouble, lon.toDouble) + } toList + } +} diff --git a/src/test/scala/org/sameersingh/scalaplot/ExampleXYTest.scala b/src/test/scala/org/sameersingh/scalaplot/ExampleXYTest.scala index 071dba3..6769fac 100644 --- a/src/test/scala/org/sameersingh/scalaplot/ExampleXYTest.scala +++ b/src/test/scala/org/sameersingh/scalaplot/ExampleXYTest.scala @@ -1,5 +1,8 @@ package org.sameersingh.scalaplot +import java.io.File + +import language.postfixOps import gnuplot.GnuplotPlotter import jfreegraph.JFGraphPlotter import org.junit._ @@ -151,6 +154,8 @@ class ExampleXYTest { import org.sameersingh.scalaplot.Implicits._ val x = 0.0 until 10.0 by 0.01 val rnd = new scala.util.Random(0) - println(output(PNG("docs/img/", "scatter"), xyChart(x -> Seq(Y(x, style = XYPlotStyle.Lines), Y(x.map(_ + rnd.nextDouble - 0.5), style = XYPlotStyle.Dots))))) + val folder = new File("doc/img/") + folder.mkdirs() + println(output(PNG(folder.getPath, "scatter"), xyChart(x -> Seq(Y(x, style = XYPlotStyle.Lines), Y(x.map(_ + rnd.nextDouble - 0.5), style = XYPlotStyle.Dots))))) } }