Can now adequately read paths from SVG
Not yet reading curvature, which will probably one day be necessary.
This commit is contained in:
parent
9892af65e3
commit
f2c39f9017
|
@ -5,6 +5,7 @@
|
||||||
(:require [clojure.string :as s]
|
(:require [clojure.string :as s]
|
||||||
[walkmap.edge :as e]
|
[walkmap.edge :as e]
|
||||||
[walkmap.polygon :refer [polygon?]]
|
[walkmap.polygon :refer [polygon?]]
|
||||||
|
[walkmap.utils :refer [kind-type]]
|
||||||
[walkmap.vertex :refer [vertex?]]))
|
[walkmap.vertex :refer [vertex?]]))
|
||||||
|
|
||||||
(defn path?
|
(defn path?
|
||||||
|
@ -30,7 +31,7 @@
|
||||||
(throw (IllegalArgumentException.
|
(throw (IllegalArgumentException.
|
||||||
(str
|
(str
|
||||||
"Each item on path must be a vertex: "
|
"Each item on path must be a vertex: "
|
||||||
(s/join " " (map #(or (:kind %) (type %) "nil") vertices)))))))
|
(s/join " " (map kind-type vertices)))))))
|
||||||
|
|
||||||
(defn polygon->path
|
(defn polygon->path
|
||||||
"If `o` is a polygon, return an equivalent path. What's different about
|
"If `o` is a polygon, return an equivalent path. What's different about
|
||||||
|
|
|
@ -7,10 +7,11 @@
|
||||||
[clojure.string :as s]
|
[clojure.string :as s]
|
||||||
[clojure.xml :as x]
|
[clojure.xml :as x]
|
||||||
[clojure.zip :as z]
|
[clojure.zip :as z]
|
||||||
[taoensso.timbre :as l :refer [info error spy]]
|
[taoensso.timbre :as l]
|
||||||
[walkmap.path :refer [path]]
|
[walkmap.path :refer [path]]
|
||||||
;; [walkmap.polygon :refer [polygon]]
|
;; [walkmap.polygon :refer [polygon]]
|
||||||
[walkmap.tag :refer [tag]]
|
[walkmap.tag :refer [tag]]
|
||||||
|
[walkmap.utils :refer [kind-type truncate]]
|
||||||
[walkmap.vertex :refer [vertex vertex?]]))
|
[walkmap.vertex :refer [vertex vertex?]]))
|
||||||
|
|
||||||
(defn upper-case?
|
(defn upper-case?
|
||||||
|
@ -19,38 +20,53 @@
|
||||||
|
|
||||||
(defn match->vertex
|
(defn match->vertex
|
||||||
[match-vector x y]
|
[match-vector x y]
|
||||||
(let [command (nth match-vector 1)
|
(when-not (empty? match-vector)
|
||||||
xcoord (read-string (nth match-vector 2))
|
(let [command (nth match-vector 1)
|
||||||
ycoord (read-string (nth match-vector 3))
|
xcoord (read-string (nth match-vector 2))
|
||||||
;; upper case command letters mean the coordinates that follow are
|
ycoord (read-string (nth match-vector 3))
|
||||||
;; absolute; lower case, relative.
|
;; upper case command letters mean the coordinates that follow are
|
||||||
x' (if (upper-case? command) xcoord (+ x xcoord))
|
;; absolute; lower case, relative.
|
||||||
y' (if (upper-case? command) ycoord (+ y ycoord))]
|
x' (if (upper-case? command) xcoord (+ x xcoord))
|
||||||
(case (s/lower-case command)
|
y' (if (upper-case? command) ycoord (+ y ycoord))]
|
||||||
("m" "l") {:vertex (vertex x' y') :x x' :y y'})))
|
(case (s/lower-case command)
|
||||||
|
("m" "l") {:vertex (vertex x' y') :x x' :y y'}
|
||||||
|
nil))))
|
||||||
|
|
||||||
(defn command-string->vertices
|
(defn command-string->vertices
|
||||||
|
"Return the destination of each successive line (`l`, `L`) and move (`m`, `M`)
|
||||||
|
command in this string `s`, expected to be an SVG path command string."
|
||||||
[s]
|
[s]
|
||||||
(let [matcher (re-matcher #"([a-zA-Z]) +([-+]?[0-9]*\.?[0-9]+) +([-+]?[0-9]*\.?[0-9]+) +" s)]
|
(let [cmd-matcher ;; matches a 'command' in the string: a letter followed by
|
||||||
(loop [match (re-find matcher) ;loop starts with 2 set arguments
|
;;spaces and numbers
|
||||||
|
(re-matcher #"[a-zA-Z][^a-zA-Z]*" s)
|
||||||
|
seg-pattern ;; matches a command which initiates a move of the current
|
||||||
|
;; position.
|
||||||
|
#"([a-zA-Z]) +([-+]?[0-9]*\.?[0-9]+) +([-+]?[0-9]*\.?[0-9]+) +"]
|
||||||
|
(loop [match (re-find cmd-matcher)
|
||||||
result []
|
result []
|
||||||
x 0
|
x 0
|
||||||
y 0]
|
y 0]
|
||||||
(if-not match
|
(if-not match
|
||||||
(filter vertex? result)
|
(filter vertex? result)
|
||||||
(let [m (match->vertex match x y)]
|
(let [m (match->vertex (re-find seg-pattern match) x y)]
|
||||||
(recur (re-find matcher) ;loop with 2 new arguments
|
(recur (re-find cmd-matcher) ;loop with 2 new arguments
|
||||||
(conj result (:vertex m))
|
(conj result (:vertex m))
|
||||||
(:x m)
|
(or (:x m) x)
|
||||||
(:y m)))))))
|
(or (:y m) y)))))))
|
||||||
|
|
||||||
(defn path-elt->path
|
(defn path-elt->path
|
||||||
|
"Given the SVG path element `elt`, return a walkmap path structure
|
||||||
|
representing the line (`l`, `L`) and move (`m`, `M`) commands in
|
||||||
|
that path."
|
||||||
[elt]
|
[elt]
|
||||||
(tag
|
(if (= (-> elt :tag) :path)
|
||||||
(path (command-string->vertices (-> elt :attrs :d)))
|
(let [vs (command-string->vertices (-> elt :attrs :d))
|
||||||
(when (-> elt :attrs :class)
|
p (when-not (empty? vs) (apply path vs))]
|
||||||
(map keyword (s/split (-> elt :attrs :class) #" ")))))
|
(if (and p (-> elt :attrs :class))
|
||||||
|
(tag p (map keyword (s/split (-> elt :attrs :class) #" ")))
|
||||||
|
p))
|
||||||
|
(throw (IllegalArgumentException.
|
||||||
|
(str "Must be an SVG `path` element: " elt)))))
|
||||||
|
|
||||||
(defn progeny
|
(defn progeny
|
||||||
"Return all the nodes in the XML structure below this `elt` which match
|
"Return all the nodes in the XML structure below this `elt` which match
|
||||||
|
@ -79,17 +95,4 @@
|
||||||
([file-name map-kind]
|
([file-name map-kind]
|
||||||
(let [xml (x/parse (io/file file-name))
|
(let [xml (x/parse (io/file file-name))
|
||||||
paths (progeny xml #(= (:tag %) :path))]
|
paths (progeny xml #(= (:tag %) :path))]
|
||||||
(map path-elt->path paths))))
|
(remove nil? (map path-elt->path paths)))))
|
||||||
|
|
||||||
(read-svg "resources/iom/manual_roads.svg")
|
|
||||||
|
|
||||||
|
|
||||||
;; (def xx (z/xml-zip (x/parse (io/file "resources/iom/manual_roads.svg"))))
|
|
||||||
|
|
||||||
;; (type xx)
|
|
||||||
;; (first xx)
|
|
||||||
|
|
||||||
;; (zx/xml-> xx :svg :g :path)
|
|
||||||
|
|
||||||
;; (def xxx (x/parse (io/file "resources/iom/manual_roads.svg")))
|
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,9 @@
|
||||||
"Code for tagging, untagging, and finding tags on objects. Note the use of
|
"Code for tagging, untagging, and finding tags on objects. Note the use of
|
||||||
the namespaced keyword, `:walkmap.tag/tags`, denoted in this file `::tags`.
|
the namespaced keyword, `:walkmap.tag/tags`, denoted in this file `::tags`.
|
||||||
This is in an attempt to avoid name clashes with other uses of this key."
|
This is in an attempt to avoid name clashes with other uses of this key."
|
||||||
(:require [clojure.set :refer [difference union]]))
|
(:require [clojure.set :refer [difference union]]
|
||||||
|
[taoensso.timbre :as l]
|
||||||
|
[walkmap.utils :refer [kind-type]]))
|
||||||
|
|
||||||
(defn tagged?
|
(defn tagged?
|
||||||
"True if this `object` is tagged with each of these `tags`. It is an error
|
"True if this `object` is tagged with each of these `tags`. It is an error
|
||||||
|
@ -20,9 +22,9 @@
|
||||||
(set? ot)
|
(set? ot)
|
||||||
(every? ot tags)))
|
(every? ot tags)))
|
||||||
(throw (IllegalArgumentException.
|
(throw (IllegalArgumentException.
|
||||||
(str "Must be keyword(s): " (map type tags)))))
|
(str "Must be keyword(s): " (map kind-type tags)))))
|
||||||
(throw (IllegalArgumentException.
|
(throw (IllegalArgumentException.
|
||||||
(str "Must be a map: " (type object))))))
|
(str "Must be a map: " (kind-type object))))))
|
||||||
|
|
||||||
(defn tag
|
(defn tag
|
||||||
"Return an object like this `object` but with these `tags` added to its tags,
|
"Return an object like this `object` but with these `tags` added to its tags,
|
||||||
|
@ -35,6 +37,7 @@
|
||||||
It's legal to include sequences of keywords in `tags`, so that users can do
|
It's legal to include sequences of keywords in `tags`, so that users can do
|
||||||
useful things like `(tag obj (map keyword some-strings))`."
|
useful things like `(tag obj (map keyword some-strings))`."
|
||||||
[object & tags]
|
[object & tags]
|
||||||
|
(l/debug "Tagging" (or (:kind object) (type object) nil) "with" tags)
|
||||||
(let [tags' (flatten tags)]
|
(let [tags' (flatten tags)]
|
||||||
(if
|
(if
|
||||||
(map? object)
|
(map? object)
|
||||||
|
@ -42,9 +45,9 @@
|
||||||
(every? keyword? tags')
|
(every? keyword? tags')
|
||||||
(assoc object ::tags (union (set tags') (::tags object)))
|
(assoc object ::tags (union (set tags') (::tags object)))
|
||||||
(throw (IllegalArgumentException.
|
(throw (IllegalArgumentException.
|
||||||
(str "Must be keyword(s): " (map type tags')))))
|
(str "Must be keyword(s): " (map kind-type tags')))))
|
||||||
(throw (IllegalArgumentException.
|
(throw (IllegalArgumentException.
|
||||||
(str "Must be a map: " (type object)))))))
|
(str "Must be a map: " (kind-type object)))))))
|
||||||
|
|
||||||
(defmacro tags
|
(defmacro tags
|
||||||
"Return the tags of this object, if any."
|
"Return the tags of this object, if any."
|
||||||
|
@ -64,6 +67,6 @@
|
||||||
(every? keyword? tags)
|
(every? keyword? tags)
|
||||||
(assoc object ::tags (difference (::tags object) (set tags)))
|
(assoc object ::tags (difference (::tags object) (set tags)))
|
||||||
(throw (IllegalArgumentException.
|
(throw (IllegalArgumentException.
|
||||||
(str "Must be keywords: " (map type tags)))))
|
(str "Must be keywords: " (map kind-type tags)))))
|
||||||
(throw (IllegalArgumentException.
|
(throw (IllegalArgumentException.
|
||||||
(str "Must be a map: " (type object))))))
|
(str "Must be a map: " (kind-type object))))))
|
||||||
|
|
|
@ -26,4 +26,10 @@
|
||||||
(subs s 0 n)
|
(subs s 0 n)
|
||||||
s))
|
s))
|
||||||
|
|
||||||
|
(defn kind-type
|
||||||
|
"Identify the type of an `object`, e.g. for logging. If it has a `:kind` key,
|
||||||
|
it's one of ours, and that's what we want. Otherwise, we want its type; but
|
||||||
|
the type of `nil` is `nil`, which doesn't get printed when assembling error
|
||||||
|
,essages, so return \"nil\"."
|
||||||
|
[object]
|
||||||
|
(or (:kind object) (type object) "nil"))
|
||||||
|
|
Loading…
Reference in a new issue