1#![doc(html_logo_url = "https://raw.githubusercontent.com/georust/meta/master/logo/logo.png")]
2#![cfg_attr(docsrs, feature(doc_auto_cfg))]
20
21#![cfg_attr(feature = "geo-types", doc = "```")]
36#![cfg_attr(not(feature = "geo-types"), doc = "```ignore")]
37#![cfg_attr(feature = "geo-types", doc = "```")]
47#![cfg_attr(not(feature = "geo-types"), doc = "```ignore")]
48use std::default::Default;
99use std::fmt;
100use std::str::FromStr;
101
102use geo_traits::{
103 GeometryCollectionTrait, GeometryTrait, LineStringTrait, MultiLineStringTrait, MultiPointTrait,
104 MultiPolygonTrait, PointTrait, PolygonTrait,
105};
106use num_traits::{Float, Num, NumCast};
107
108use crate::to_wkt::write_geometry;
109use crate::tokenizer::{PeekableTokens, Token, Tokens};
110use crate::types::{
111 Dimension, GeometryCollection, LineString, MultiLineString, MultiPoint, MultiPolygon, Point,
112 Polygon,
113};
114
115pub mod to_wkt;
116mod tokenizer;
117
118pub mod error;
120pub mod types;
122
123mod infer_type;
124
125pub use infer_type::infer_type;
126
127#[cfg(feature = "geo-types")]
128extern crate geo_types;
129
130pub use crate::to_wkt::ToWkt;
131
132#[cfg(feature = "geo-types")]
133#[deprecated(note = "renamed module to `wkt::geo_types_from_wkt`")]
134pub mod conversion;
135#[cfg(feature = "geo-types")]
136pub mod geo_types_from_wkt;
137#[cfg(feature = "geo-types")]
138mod geo_types_to_wkt;
139
140#[cfg(feature = "serde")]
141extern crate serde;
142#[cfg(feature = "serde")]
143pub mod deserialize;
144#[cfg(feature = "serde")]
145pub use deserialize::deserialize_wkt;
146
147mod from_wkt;
148pub use from_wkt::TryFromWkt;
149
150#[cfg(all(feature = "serde", feature = "geo-types"))]
151#[allow(deprecated)]
152pub use deserialize::geo_types::deserialize_geometry;
153
154#[cfg(all(feature = "serde", feature = "geo-types"))]
155#[deprecated(
156 since = "0.10.2",
157 note = "instead: use wkt::deserialize::geo_types::deserialize_point"
158)]
159pub use deserialize::geo_types::deserialize_point;
160
161pub trait WktNum: Num + NumCast + PartialOrd + PartialEq + Copy + fmt::Debug {}
162impl<T> WktNum for T where T: Num + NumCast + PartialOrd + PartialEq + Copy + fmt::Debug {}
163
164pub trait WktFloat: WktNum + Float {}
165impl<T> WktFloat for T where T: WktNum + Float {}
166
167#[derive(Clone, Debug, PartialEq)]
168pub enum Wkt<T>
170where
171 T: WktNum,
172{
173 Point(Point<T>),
174 LineString(LineString<T>),
175 Polygon(Polygon<T>),
176 MultiPoint(MultiPoint<T>),
177 MultiLineString(MultiLineString<T>),
178 MultiPolygon(MultiPolygon<T>),
179 GeometryCollection(GeometryCollection<T>),
180}
181
182impl<T> Wkt<T>
183where
184 T: WktNum + FromStr + Default,
185{
186 fn from_word_and_tokens(
187 word: &str,
188 tokens: &mut PeekableTokens<T>,
189 ) -> Result<Self, &'static str> {
190 match word {
195 w if w.eq_ignore_ascii_case("POINT") => {
196 let x = <Point<T> as FromTokens<T>>::from_tokens_with_header(tokens, None);
197 x.map(|y| y.into())
198 }
199 w if w.eq_ignore_ascii_case("POINTZ") => {
200 let x = <Point<T> as FromTokens<T>>::from_tokens_with_header(
201 tokens,
202 Some(Dimension::XYZ),
203 );
204 x.map(|y| y.into())
205 }
206 w if w.eq_ignore_ascii_case("POINTM") => {
207 let x = <Point<T> as FromTokens<T>>::from_tokens_with_header(
208 tokens,
209 Some(Dimension::XYM),
210 );
211 x.map(|y| y.into())
212 }
213 w if w.eq_ignore_ascii_case("POINTZM") => {
214 let x = <Point<T> as FromTokens<T>>::from_tokens_with_header(
215 tokens,
216 Some(Dimension::XYZM),
217 );
218 x.map(|y| y.into())
219 }
220 w if w.eq_ignore_ascii_case("LINESTRING") || w.eq_ignore_ascii_case("LINEARRING") => {
221 let x = <LineString<T> as FromTokens<T>>::from_tokens_with_header(tokens, None);
222 x.map(|y| y.into())
223 }
224 w if w.eq_ignore_ascii_case("LINESTRINGZ") => {
225 let x = <LineString<T> as FromTokens<T>>::from_tokens_with_header(
226 tokens,
227 Some(Dimension::XYZ),
228 );
229 x.map(|y| y.into())
230 }
231 w if w.eq_ignore_ascii_case("LINESTRINGM") => {
232 let x = <LineString<T> as FromTokens<T>>::from_tokens_with_header(
233 tokens,
234 Some(Dimension::XYM),
235 );
236 x.map(|y| y.into())
237 }
238 w if w.eq_ignore_ascii_case("LINESTRINGZM") => {
239 let x = <LineString<T> as FromTokens<T>>::from_tokens_with_header(
240 tokens,
241 Some(Dimension::XYZM),
242 );
243 x.map(|y| y.into())
244 }
245 w if w.eq_ignore_ascii_case("POLYGON") => {
246 let x = <Polygon<T> as FromTokens<T>>::from_tokens_with_header(tokens, None);
247 x.map(|y| y.into())
248 }
249 w if w.eq_ignore_ascii_case("POLYGONZ") => {
250 let x = <Polygon<T> as FromTokens<T>>::from_tokens_with_header(
251 tokens,
252 Some(Dimension::XYZ),
253 );
254 x.map(|y| y.into())
255 }
256 w if w.eq_ignore_ascii_case("POLYGONM") => {
257 let x = <Polygon<T> as FromTokens<T>>::from_tokens_with_header(
258 tokens,
259 Some(Dimension::XYM),
260 );
261 x.map(|y| y.into())
262 }
263 w if w.eq_ignore_ascii_case("POLYGONZM") => {
264 let x = <Polygon<T> as FromTokens<T>>::from_tokens_with_header(
265 tokens,
266 Some(Dimension::XYZM),
267 );
268 x.map(|y| y.into())
269 }
270 w if w.eq_ignore_ascii_case("MULTIPOINT") => {
271 let x = <MultiPoint<T> as FromTokens<T>>::from_tokens_with_header(tokens, None);
272 x.map(|y| y.into())
273 }
274 w if w.eq_ignore_ascii_case("MULTIPOINTZ") => {
275 let x = <MultiPoint<T> as FromTokens<T>>::from_tokens_with_header(
276 tokens,
277 Some(Dimension::XYZ),
278 );
279 x.map(|y| y.into())
280 }
281 w if w.eq_ignore_ascii_case("MULTIPOINTM") => {
282 let x = <MultiPoint<T> as FromTokens<T>>::from_tokens_with_header(
283 tokens,
284 Some(Dimension::XYM),
285 );
286 x.map(|y| y.into())
287 }
288 w if w.eq_ignore_ascii_case("MULTIPOINTZM") => {
289 let x = <MultiPoint<T> as FromTokens<T>>::from_tokens_with_header(
290 tokens,
291 Some(Dimension::XYZM),
292 );
293 x.map(|y| y.into())
294 }
295 w if w.eq_ignore_ascii_case("MULTILINESTRING") => {
296 let x =
297 <MultiLineString<T> as FromTokens<T>>::from_tokens_with_header(tokens, None);
298 x.map(|y| y.into())
299 }
300 w if w.eq_ignore_ascii_case("MULTILINESTRINGZ") => {
301 let x = <MultiLineString<T> as FromTokens<T>>::from_tokens_with_header(
302 tokens,
303 Some(Dimension::XYZ),
304 );
305 x.map(|y| y.into())
306 }
307 w if w.eq_ignore_ascii_case("MULTILINESTRINGM") => {
308 let x = <MultiLineString<T> as FromTokens<T>>::from_tokens_with_header(
309 tokens,
310 Some(Dimension::XYM),
311 );
312 x.map(|y| y.into())
313 }
314 w if w.eq_ignore_ascii_case("MULTILINESTRINGZM") => {
315 let x = <MultiLineString<T> as FromTokens<T>>::from_tokens_with_header(
316 tokens,
317 Some(Dimension::XYZM),
318 );
319 x.map(|y| y.into())
320 }
321 w if w.eq_ignore_ascii_case("MULTIPOLYGON") => {
322 let x = <MultiPolygon<T> as FromTokens<T>>::from_tokens_with_header(tokens, None);
323 x.map(|y| y.into())
324 }
325 w if w.eq_ignore_ascii_case("MULTIPOLYGONZ") => {
326 let x = <MultiPolygon<T> as FromTokens<T>>::from_tokens_with_header(
327 tokens,
328 Some(Dimension::XYZ),
329 );
330 x.map(|y| y.into())
331 }
332 w if w.eq_ignore_ascii_case("MULTIPOLYGONM") => {
333 let x = <MultiPolygon<T> as FromTokens<T>>::from_tokens_with_header(
334 tokens,
335 Some(Dimension::XYM),
336 );
337 x.map(|y| y.into())
338 }
339 w if w.eq_ignore_ascii_case("MULTIPOLYGONZM") => {
340 let x = <MultiPolygon<T> as FromTokens<T>>::from_tokens_with_header(
341 tokens,
342 Some(Dimension::XYZM),
343 );
344 x.map(|y| y.into())
345 }
346 w if w.eq_ignore_ascii_case("GEOMETRYCOLLECTION") => {
347 let x =
348 <GeometryCollection<T> as FromTokens<T>>::from_tokens_with_header(tokens, None);
349 x.map(|y| y.into())
350 }
351 w if w.eq_ignore_ascii_case("GEOMETRYCOLLECTIONZ") => {
352 let x = <GeometryCollection<T> as FromTokens<T>>::from_tokens_with_header(
353 tokens,
354 Some(Dimension::XYZ),
355 );
356 x.map(|y| y.into())
357 }
358 w if w.eq_ignore_ascii_case("GEOMETRYCOLLECTIONM") => {
359 let x = <GeometryCollection<T> as FromTokens<T>>::from_tokens_with_header(
360 tokens,
361 Some(Dimension::XYM),
362 );
363 x.map(|y| y.into())
364 }
365 w if w.eq_ignore_ascii_case("GEOMETRYCOLLECTIONZM") => {
366 let x = <GeometryCollection<T> as FromTokens<T>>::from_tokens_with_header(
367 tokens,
368 Some(Dimension::XYZM),
369 );
370 x.map(|y| y.into())
371 }
372 _ => Err("Invalid type encountered"),
373 }
374 }
375}
376
377impl<T> fmt::Display for Wkt<T>
378where
379 T: WktNum + fmt::Display,
380{
381 fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
382 Ok(write_geometry(f, self)?)
383 }
384}
385
386impl<T> Wkt<T>
387where
388 T: WktNum + FromStr + Default,
389{
390 fn from_tokens(tokens: Tokens<T>) -> Result<Self, &'static str> {
391 let mut tokens = tokens.peekable();
392 let word = match tokens.next().transpose()? {
393 Some(Token::Word(word)) => {
394 if !word.is_ascii() {
395 return Err("Encountered non-ascii word");
396 }
397 word
398 }
399 _ => return Err("Invalid WKT format"),
400 };
401 Wkt::from_word_and_tokens(&word, &mut tokens)
402 }
403}
404
405impl<T> FromStr for Wkt<T>
406where
407 T: WktNum + FromStr + Default,
408{
409 type Err = &'static str;
410
411 fn from_str(wkt_str: &str) -> Result<Self, Self::Err> {
412 Wkt::from_tokens(Tokens::from_str(wkt_str))
413 }
414}
415
416impl<T: WktNum> GeometryTrait for Wkt<T> {
417 type T = T;
418 type PointType<'b> = Point<T> where Self: 'b;
419 type LineStringType<'b> = LineString<T> where Self: 'b;
420 type PolygonType<'b> = Polygon<T> where Self: 'b;
421 type MultiPointType<'b> = MultiPoint<T> where Self: 'b;
422 type MultiLineStringType<'b> = MultiLineString<T> where Self: 'b;
423 type MultiPolygonType<'b> = MultiPolygon<T> where Self: 'b;
424 type GeometryCollectionType<'b> = GeometryCollection<T> where Self: 'b;
425 type RectType<'b> = geo_traits::UnimplementedRect<T> where Self: 'b;
426 type LineType<'b> = geo_traits::UnimplementedLine<T> where Self: 'b;
427 type TriangleType<'b> = geo_traits::UnimplementedTriangle<T> where Self: 'b;
428
429 fn dim(&self) -> geo_traits::Dimensions {
430 match self {
431 Wkt::Point(geom) => PointTrait::dim(geom),
432 Wkt::LineString(geom) => LineStringTrait::dim(geom),
433 Wkt::Polygon(geom) => PolygonTrait::dim(geom),
434 Wkt::MultiPoint(geom) => MultiPointTrait::dim(geom),
435 Wkt::MultiLineString(geom) => MultiLineStringTrait::dim(geom),
436 Wkt::MultiPolygon(geom) => MultiPolygonTrait::dim(geom),
437 Wkt::GeometryCollection(geom) => GeometryCollectionTrait::dim(geom),
438 }
439 }
440
441 fn as_type(
442 &self,
443 ) -> geo_traits::GeometryType<
444 '_,
445 Point<T>,
446 LineString<T>,
447 Polygon<T>,
448 MultiPoint<T>,
449 MultiLineString<T>,
450 MultiPolygon<T>,
451 GeometryCollection<T>,
452 Self::RectType<'_>,
453 Self::TriangleType<'_>,
454 Self::LineType<'_>,
455 > {
456 match self {
457 Wkt::Point(geom) => geo_traits::GeometryType::Point(geom),
458 Wkt::LineString(geom) => geo_traits::GeometryType::LineString(geom),
459 Wkt::Polygon(geom) => geo_traits::GeometryType::Polygon(geom),
460 Wkt::MultiPoint(geom) => geo_traits::GeometryType::MultiPoint(geom),
461 Wkt::MultiLineString(geom) => geo_traits::GeometryType::MultiLineString(geom),
462 Wkt::MultiPolygon(geom) => geo_traits::GeometryType::MultiPolygon(geom),
463 Wkt::GeometryCollection(geom) => geo_traits::GeometryType::GeometryCollection(geom),
464 }
465 }
466}
467
468impl<T: WktNum> GeometryTrait for &Wkt<T> {
469 type T = T;
470 type PointType<'b> = Point<T> where Self: 'b;
471 type LineStringType<'b> = LineString<T> where Self: 'b;
472 type PolygonType<'b> = Polygon<T> where Self: 'b;
473 type MultiPointType<'b> = MultiPoint<T> where Self: 'b;
474 type MultiLineStringType<'b> = MultiLineString<T> where Self: 'b;
475 type MultiPolygonType<'b> = MultiPolygon<T> where Self: 'b;
476 type GeometryCollectionType<'b> = GeometryCollection<T> where Self: 'b;
477 type RectType<'b> = geo_traits::UnimplementedRect<T> where Self: 'b;
478 type LineType<'b> = geo_traits::UnimplementedLine<T> where Self: 'b;
479 type TriangleType<'b> = geo_traits::UnimplementedTriangle<T> where Self: 'b;
480
481 fn dim(&self) -> geo_traits::Dimensions {
482 match self {
483 Wkt::Point(geom) => PointTrait::dim(geom),
484 Wkt::LineString(geom) => LineStringTrait::dim(geom),
485 Wkt::Polygon(geom) => PolygonTrait::dim(geom),
486 Wkt::MultiPoint(geom) => MultiPointTrait::dim(geom),
487 Wkt::MultiLineString(geom) => MultiLineStringTrait::dim(geom),
488 Wkt::MultiPolygon(geom) => MultiPolygonTrait::dim(geom),
489 Wkt::GeometryCollection(geom) => GeometryCollectionTrait::dim(geom),
490 }
491 }
492
493 fn as_type(
494 &self,
495 ) -> geo_traits::GeometryType<
496 '_,
497 Point<T>,
498 LineString<T>,
499 Polygon<T>,
500 MultiPoint<T>,
501 MultiLineString<T>,
502 MultiPolygon<T>,
503 GeometryCollection<T>,
504 Self::RectType<'_>,
505 Self::TriangleType<'_>,
506 Self::LineType<'_>,
507 > {
508 match self {
509 Wkt::Point(geom) => geo_traits::GeometryType::Point(geom),
510 Wkt::LineString(geom) => geo_traits::GeometryType::LineString(geom),
511 Wkt::Polygon(geom) => geo_traits::GeometryType::Polygon(geom),
512 Wkt::MultiPoint(geom) => geo_traits::GeometryType::MultiPoint(geom),
513 Wkt::MultiLineString(geom) => geo_traits::GeometryType::MultiLineString(geom),
514 Wkt::MultiPolygon(geom) => geo_traits::GeometryType::MultiPolygon(geom),
515 Wkt::GeometryCollection(geom) => geo_traits::GeometryType::GeometryCollection(geom),
516 }
517 }
518}
519
520macro_rules! impl_specialization {
523 ($geometry_type:ident) => {
524 impl<T: WktNum> GeometryTrait for $geometry_type<T> {
525 type T = T;
526 type PointType<'b> = Point<Self::T> where Self: 'b;
527 type LineStringType<'b> = LineString<Self::T> where Self: 'b;
528 type PolygonType<'b> = Polygon<Self::T> where Self: 'b;
529 type MultiPointType<'b> = MultiPoint<Self::T> where Self: 'b;
530 type MultiLineStringType<'b> = MultiLineString<Self::T> where Self: 'b;
531 type MultiPolygonType<'b> = MultiPolygon<Self::T> where Self: 'b;
532 type GeometryCollectionType<'b> = GeometryCollection<Self::T> where Self: 'b;
533 type RectType<'b> = geo_traits::UnimplementedRect<T> where Self: 'b;
534 type LineType<'b> = geo_traits::UnimplementedLine<T> where Self: 'b;
535 type TriangleType<'b> = geo_traits::UnimplementedTriangle<T> where Self: 'b;
536
537 fn dim(&self) -> geo_traits::Dimensions {
538 geo_traits::Dimensions::Xy
539 }
540
541 fn as_type(
542 &self,
543 ) -> geo_traits::GeometryType<
544 '_,
545 Point<T>,
546 LineString<T>,
547 Polygon<T>,
548 MultiPoint<T>,
549 MultiLineString<T>,
550 MultiPolygon<T>,
551 GeometryCollection<T>,
552 Self::RectType<'_>,
553 Self::TriangleType<'_>,
554 Self::LineType<'_>,
555 > {
556 geo_traits::GeometryType::$geometry_type(self)
557 }
558 }
559
560 impl<'a, T: WktNum + 'a> GeometryTrait for &'a $geometry_type<T> {
561 type T = T;
562 type PointType<'b> = Point<Self::T> where Self: 'b;
563 type LineStringType<'b> = LineString<Self::T> where Self: 'b;
564 type PolygonType<'b> = Polygon<Self::T> where Self: 'b;
565 type MultiPointType<'b> = MultiPoint<Self::T> where Self: 'b;
566 type MultiLineStringType<'b> = MultiLineString<Self::T> where Self: 'b;
567 type MultiPolygonType<'b> = MultiPolygon<Self::T> where Self: 'b;
568 type GeometryCollectionType<'b> = GeometryCollection<Self::T> where Self: 'b;
569 type RectType<'b> = geo_traits::UnimplementedRect<T> where Self: 'b;
570 type LineType<'b> = geo_traits::UnimplementedLine<T> where Self: 'b;
571 type TriangleType<'b> = geo_traits::UnimplementedTriangle<T> where Self: 'b;
572
573 fn dim(&self) -> geo_traits::Dimensions {
574 geo_traits::Dimensions::Xy
575 }
576
577 fn as_type(
578 &self,
579 ) -> geo_traits::GeometryType<
580 '_,
581 Point<T>,
582 LineString<T>,
583 Polygon<T>,
584 MultiPoint<T>,
585 MultiLineString<T>,
586 MultiPolygon<T>,
587 GeometryCollection<T>,
588 Self::RectType<'_>,
589 Self::TriangleType<'_>,
590 Self::LineType<'_>,
591 > {
592 geo_traits::GeometryType::$geometry_type(self)
593 }
594 }
595 };
596}
597
598impl_specialization!(Point);
599impl_specialization!(LineString);
600impl_specialization!(Polygon);
601impl_specialization!(MultiPoint);
602impl_specialization!(MultiLineString);
603impl_specialization!(MultiPolygon);
604impl_specialization!(GeometryCollection);
605
606fn infer_geom_dimension<T: WktNum + FromStr + Default>(
607 tokens: &mut PeekableTokens<T>,
608) -> Result<Dimension, &'static str> {
609 if let Some(Ok(c)) = tokens.peek() {
610 match c {
611 Token::Word(w) => match w.as_str() {
613 w if w.eq_ignore_ascii_case("Z") => {
614 tokens.next().unwrap().unwrap();
615 Ok(Dimension::XYZ)
616 }
617 w if w.eq_ignore_ascii_case("M") => {
618 tokens.next().unwrap().unwrap();
619
620 Ok(Dimension::XYM)
621 }
622 w if w.eq_ignore_ascii_case("ZM") => {
623 tokens.next().unwrap().unwrap();
624 Ok(Dimension::XYZM)
625 }
626 w if w.eq_ignore_ascii_case("EMPTY") => Ok(Dimension::XY),
627 _ => Err("Unexpected word before open paren"),
628 },
629 _ => Ok(Dimension::XY),
631 }
632 } else {
633 Err("End of stream")
634 }
635}
636
637trait FromTokens<T>: Sized + Default
638where
639 T: WktNum + FromStr + Default,
640{
641 fn from_tokens(tokens: &mut PeekableTokens<T>, dim: Dimension) -> Result<Self, &'static str>;
642
643 fn from_tokens_with_header(
646 tokens: &mut PeekableTokens<T>,
647 dim: Option<Dimension>,
648 ) -> Result<Self, &'static str> {
649 let dim = if let Some(dim) = dim {
650 dim
651 } else {
652 infer_geom_dimension(tokens)?
653 };
654 FromTokens::from_tokens_with_parens(tokens, dim)
655 }
656
657 fn from_tokens_with_parens(
658 tokens: &mut PeekableTokens<T>,
659 dim: Dimension,
660 ) -> Result<Self, &'static str> {
661 match tokens.next().transpose()? {
662 Some(Token::ParenOpen) => (),
663 Some(Token::Word(ref s)) if s.eq_ignore_ascii_case("EMPTY") => {
664 return Ok(Default::default());
668 }
669 _ => return Err("Missing open parenthesis for type"),
670 };
671 let result = FromTokens::from_tokens(tokens, dim);
672 match tokens.next().transpose()? {
673 Some(Token::ParenClose) => (),
674 _ => return Err("Missing closing parenthesis for type"),
675 };
676 result
677 }
678
679 fn from_tokens_with_optional_parens(
680 tokens: &mut PeekableTokens<T>,
681 dim: Dimension,
682 ) -> Result<Self, &'static str> {
683 match tokens.peek() {
684 Some(Ok(Token::ParenOpen)) => Self::from_tokens_with_parens(tokens, dim),
685 _ => Self::from_tokens(tokens, dim),
686 }
687 }
688
689 fn comma_many<F>(
690 f: F,
691 tokens: &mut PeekableTokens<T>,
692 dim: Dimension,
693 ) -> Result<Vec<Self>, &'static str>
694 where
695 F: Fn(&mut PeekableTokens<T>, Dimension) -> Result<Self, &'static str>,
696 {
697 let mut items = Vec::new();
698
699 let item = f(tokens, dim)?;
700 items.push(item);
701
702 while let Some(&Ok(Token::Comma)) = tokens.peek() {
703 tokens.next(); let item = f(tokens, dim)?;
706 items.push(item);
707 }
708
709 Ok(items)
710 }
711}
712
713#[cfg(test)]
714mod tests {
715 use crate::types::{Coord, MultiPolygon, Point};
716 use crate::Wkt;
717 use std::str::FromStr;
718
719 #[test]
720 fn empty_string() {
721 let res: Result<Wkt<f64>, _> = Wkt::from_str("");
722 assert!(res.is_err());
723 }
724
725 #[test]
726 fn empty_items() {
727 let wkt: Wkt<f64> = Wkt::from_str("POINT EMPTY").ok().unwrap();
728 match wkt {
729 Wkt::Point(Point(None)) => (),
730 _ => unreachable!(),
731 };
732
733 let wkt: Wkt<f64> = Wkt::from_str("MULTIPOLYGON EMPTY").ok().unwrap();
734 match wkt {
735 Wkt::MultiPolygon(MultiPolygon(polygons)) => assert_eq!(polygons.len(), 0),
736 _ => unreachable!(),
737 };
738 }
739
740 #[test]
741 fn lowercase_point() {
742 let wkt: Wkt<f64> = Wkt::from_str("point EMPTY").ok().unwrap();
743 match wkt {
744 Wkt::Point(Point(None)) => (),
745 _ => unreachable!(),
746 };
747 }
748
749 #[test]
750 fn invalid_number() {
751 let msg = <Wkt<f64>>::from_str("POINT (10 20.1A)").unwrap_err();
752 assert_eq!(
753 "Unable to parse input number as the desired output type",
754 msg
755 );
756 }
757
758 #[test]
759 fn test_points() {
760 let wkt = <Wkt<f64>>::from_str("POINT (10 20.1)").ok().unwrap();
762 match wkt {
763 Wkt::Point(Point(Some(coord))) => {
764 assert_eq!(coord.x, 10.0);
765 assert_eq!(coord.y, 20.1);
766 assert_eq!(coord.z, None);
767 assert_eq!(coord.m, None);
768 }
769 _ => panic!("excepted to be parsed as a POINT"),
770 }
771
772 let wkt = <Wkt<f64>>::from_str("POINT Z (10 20.1 5)").ok().unwrap();
774 match wkt {
775 Wkt::Point(Point(Some(coord))) => {
776 assert_eq!(coord.x, 10.0);
777 assert_eq!(coord.y, 20.1);
778 assert_eq!(coord.z, Some(5.0));
779 assert_eq!(coord.m, None);
780 }
781 _ => panic!("excepted to be parsed as a POINT"),
782 }
783
784 let wkt = <Wkt<f64>>::from_str("POINT M (10 20.1 80)").ok().unwrap();
786 match wkt {
787 Wkt::Point(Point(Some(coord))) => {
788 assert_eq!(coord.x, 10.0);
789 assert_eq!(coord.y, 20.1);
790 assert_eq!(coord.z, None);
791 assert_eq!(coord.m, Some(80.0));
792 }
793 _ => panic!("excepted to be parsed as a POINT"),
794 }
795
796 let wkt = <Wkt<f64>>::from_str("POINT ZM (10 20.1 5 80)")
798 .ok()
799 .unwrap();
800 match wkt {
801 Wkt::Point(Point(Some(coord))) => {
802 assert_eq!(coord.x, 10.0);
803 assert_eq!(coord.y, 20.1);
804 assert_eq!(coord.z, Some(5.0));
805 assert_eq!(coord.m, Some(80.0));
806 }
807 _ => panic!("excepted to be parsed as a POINT"),
808 }
809 }
810
811 #[test]
812 fn support_jts_linearring() {
813 let wkt: Wkt<f64> = Wkt::from_str("linearring (10 20, 30 40)").ok().unwrap();
814 match wkt {
815 Wkt::LineString(_ls) => (),
816 _ => panic!("expected to be parsed as a LINESTRING"),
817 };
818 }
819
820 #[test]
821 fn test_debug() {
822 let g = Wkt::Point(Point(Some(Coord {
823 x: 1.0,
824 y: 2.0,
825 m: None,
826 z: None,
827 })));
828 assert_eq!(
829 format!("{:?}", g),
830 "Point(Point(Some(Coord { x: 1.0, y: 2.0, z: None, m: None })))"
831 );
832 }
833
834 #[test]
835 fn test_display_on_wkt() {
836 let wktls: Wkt<f64> = Wkt::from_str("LINESTRING(10 20, 20 30)").unwrap();
837
838 assert_eq!(wktls.to_string(), "LINESTRING(10 20,20 30)");
839 }
840}