Skip to content

Commit 75fb752

Browse files
committed
Performance improvements in CSS compiler
1 parent df03fec commit 75fb752

File tree

5 files changed

+62
-26
lines changed

5 files changed

+62
-26
lines changed

geocss/src/main/scala/org/geoscript/geocss/Converter.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ object Converter {
4444
writer.close()
4545
}
4646

47-
def main(args: Array[String]) = {
47+
def main(args: Array[String]) {
4848
val (options, filenames) = parse(args)
4949

5050
val write: (Seq[Rule], java.net.URL, OutputStream) => Unit =

geocss/src/main/scala/org/geoscript/geocss/CssParser.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ object CssParser extends RegexParsers {
118118
op <- "[><=]".r
119119
num <- number
120120
_ <- literal("]")
121-
} yield PseudoSelector(id, op, num)
121+
} yield PseudoSelector(id, op, num.toDouble)
122122

123123
val pseudoClass = (":" ~> identifier) ^^ PseudoClass
124124

geocss/src/main/scala/org/geoscript/geocss/Selector.scala

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,9 @@ object Selector {
4040
case (Not(p), Not(q)) => implies(q, p)
4141
case (p, Not(q)) => !allows(p, q)
4242
case (PseudoSelector("scale", ">", a), PseudoSelector("scale", ">", b)) =>
43-
b.toDouble <= a.toDouble
43+
b <= a
4444
case (PseudoSelector("scale", "<", a), PseudoSelector("scale", "<", b)) =>
45-
b.toDouble >= a.toDouble
45+
b >= a
4646
case (DataFilter(f), DataFilter(g)) =>
4747
try {
4848
given(f).reduce(g) == ogc.Filter.INCLUDE
@@ -68,9 +68,9 @@ object Selector {
6868
case (Not(p), Not(q)) => allows(q, p)
6969
case (Not(p), q) => !implies(q, p)
7070
case (PseudoSelector("scale", ">", a), PseudoSelector("scale", "<", b)) =>
71-
b.toDouble > a.toDouble
71+
b > a
7272
case (PseudoSelector("scale", "<", a), PseudoSelector("scale", ">", b)) =>
73-
b.toDouble < a.toDouble
73+
b < a
7474
case (DataFilter(f), DataFilter(g)) =>
7575
try {
7676
given(f).reduce(g) != ogc.Filter.EXCLUDE
@@ -247,7 +247,7 @@ case class Typename(typename: String) extends MetaSelector {
247247
* property such as the scale denominator at render time. This corresponds to
248248
* the [&64;scale &gt; 10000] syntax in CSS, for example.
249249
*/
250-
case class PseudoSelector(property: String, operator: String, value: String)
250+
case class PseudoSelector(property: String, operator: String, value: Double)
251251
extends MetaSelector {
252252
override def toString = "@%s%s%s".format(property, operator, value)
253253
}

geocss/src/main/scala/org/geoscript/geocss/Translator.scala

Lines changed: 36 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -590,8 +590,8 @@ class Translator(val baseURL: Option[java.net.URL]) {
590590
val scales =
591591
flatten(And(rule.selectors))
592592
.collect {
593-
case PseudoSelector("scale", _, d) => d.toDouble
594-
case Not(PseudoSelector("scale", _, d)) => d.toDouble
593+
case PseudoSelector("scale", _, d) => d
594+
case Not(PseudoSelector("scale", _, d)) => d
595595
}
596596
.sorted
597597
.distinct
@@ -613,8 +613,8 @@ class Translator(val baseURL: Option[java.net.URL]) {
613613
for {
614614
(rule, syms) <- group if syms.nonEmpty
615615
range @ (min, max) <- extractScaleRanges(rule)
616-
minSelector = min.map(x => PseudoSelector("scale", ">", x.toString))
617-
maxSelector = max.map(x => PseudoSelector("scale", "<", x.toString))
616+
minSelector = min.map(x => PseudoSelector("scale", ">", x))
617+
maxSelector = max.map(x => PseudoSelector("scale", "<", x))
618618
filter = reduce(allOf(rule.selectors ++ minSelector ++ maxSelector))
619619
if (filter != Exclude)
620620
} yield createSLDRule(min, max, realize(filter), rule.description.title, rule.description.abstrakt, syms)
@@ -687,7 +687,7 @@ class Translator(val baseURL: Option[java.net.URL]) {
687687
}
688688
reduced match {
689689
case And(sels) => sels
690-
case sel => Seq(sel)
690+
case sel => Seq(sel)
691691
}
692692
}
693693
}
@@ -736,7 +736,9 @@ class Translator(val baseURL: Option[java.net.URL]) {
736736
reduce[Selector](And(a.selectors ++ b.selectors)) == Exclude
737737

738738
val cliques = maximalCliques(xs.toSet, mutuallyExclusive)
739-
val combinations = enumerateCombinations(cliques)
739+
val combinations = visit(cliques).map(_.toSet).toSet
740+
// val oldCombinations = enumerateCombinations(cliques)
741+
// println(s"${oldCombinations.size}, ${combinations.size}")
740742

741743
val ExclusiveRule = EmptyRule.copy(selectors = Seq(Exclude))
742744

@@ -751,13 +753,39 @@ class Translator(val baseURL: Option[java.net.URL]) {
751753
for {
752754
combo <- combinations
753755
remainder = xs filterNot(combo contains _)
754-
included = include(combo)
756+
included = include(combo.toSet)
755757
excluded = exclude(remainder)
756758
constrained = constrain(included, excluded)
757759
ruleset = simplifySelector(constrained)
758-
if ruleset.isSatisfiable
760+
if ruleset.isSatisfiable
759761
} yield ruleset
760762

763+
// println(rulesets.size)
764+
// rulesets.toSeq
765+
Nil
761766
rulesets.toSeq
762767
}
768+
769+
import scala.annotation.tailrec
770+
771+
// @tailrec
772+
def visit(cliques: Set[Set[Rule]]): Seq[List[Rule]] = {
773+
def work[Rule](path: List[Rule], cliques: List[Set[Rule]]): Seq[List[Rule]] =
774+
cliques match {
775+
case Nil =>
776+
Seq(path)
777+
case top :: remainingCliques =>
778+
val includingThisLevel: Seq[List[Rule]] =
779+
top.toSeq flatMap { i =>
780+
val culledRemaining =
781+
remainingCliques.filterNot(_ contains i).map(_ -- top)
782+
work(i :: path, culledRemaining) // remainingCliques filterNot (_ contains i))
783+
}
784+
val excludingThisLevel: Seq[List[Rule]] =
785+
work(path, remainingCliques)
786+
787+
includingThisLevel ++ excludingThisLevel
788+
}
789+
work(Nil, cliques.toList.sortBy(- _.size))
790+
}
763791
}

geocss/src/main/scala/org/geoscript/geocss/filter/package.scala

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -134,15 +134,21 @@ package object filter {
134134
}
135135

136136
class Value(val text: String) {
137+
lazy val asDouble =
138+
try {
139+
Some(text.toDouble)
140+
} catch {
141+
case (_: NumberFormatException) => None
142+
}
143+
137144
override def toString = text
138145
override def equals(that: Any) =
139146
that match {
140147
case (that: Value) =>
141-
try {
142-
this.text.toDouble == that.text.toDouble
143-
} catch {
144-
case (_: NumberFormatException) => this.text == that.text
145-
}
148+
if (this.asDouble.isDefined && that.asDouble.isDefined)
149+
this.asDouble == that.asDouble
150+
else
151+
this.text == that.text
146152
case _ => false
147153
}
148154
}
@@ -153,12 +159,14 @@ package object filter {
153159
apply(literal.getValue.toString)
154160

155161
implicit val valuesAreOrdered: Ordering[Value] =
156-
Ordering.fromLessThan { (a: Value, b: Value) =>
157-
try {
158-
a.text.toDouble < b.text.toDouble
159-
} catch {
160-
case (_: NumberFormatException) => a.text < b.text
161-
}
162+
new Ordering[Value] {
163+
val dbl = implicitly[Ordering[Double]]
164+
val str = implicitly[Ordering[String]]
165+
def compare(x: Value, y: Value): Int =
166+
if (x.asDouble.isDefined && y.asDouble.isDefined)
167+
dbl.compare(x.asDouble.get, y.asDouble.get)
168+
else
169+
str.compare(x.text, y.text)
162170
}
163171
}
164172
case object Unconstrained extends Constraint

0 commit comments

Comments
 (0)