From 20283c6cf5c6951cc1f2787492c67a7fb72aee9a Mon Sep 17 00:00:00 2001 From: Thibaut GRIDEL Date: Mon, 1 Nov 2010 15:50:45 +0100 Subject: [PATCH] Imported Upstream version 1.4.1 --- ChangeLog | 1732 ++++++++++++++++++++ INSTALL.txt | 1 + Makefile | 45 + NEWS.txt | 1 + README.txt | 1 + agpl-3.0.txt | 661 ++++++++ doc/ALGORITHM.txt | 212 +++ doc/CONFIGURATION.txt | 177 ++ doc/DATA.txt | 115 ++ doc/INSTALL.txt | 158 ++ doc/Makefile | 48 + doc/NEWS.txt | 177 ++ doc/OUTPUT.txt | 250 +++ doc/README.txt | 147 ++ doc/TAGGING.txt | 352 ++++ doc/USAGE.txt | 425 +++++ doc/html/algorithm.html | 266 +++ doc/html/configuration.html | 256 +++ doc/html/data.html | 165 ++ doc/html/example0.png | Bin 0 -> 11045 bytes doc/html/example1.png | Bin 0 -> 5152 bytes doc/html/example2.png | Bin 0 -> 5019 bytes doc/html/index.html | 124 ++ doc/html/installation.html | 243 +++ doc/html/output.html | 364 ++++ doc/html/style.css | 391 +++++ doc/html/tagging.html | 658 ++++++++ doc/html/usage.html | 443 +++++ src/Makefile | 154 ++ src/filedumper.c | 674 ++++++++ src/files.c | 372 +++++ src/functions.h | 106 ++ src/functionsx.h | 39 + src/nodes.c | 498 ++++++ src/nodes.h | 94 ++ src/nodesx.c | 894 ++++++++++ src/nodesx.h | 100 ++ src/optimiser.c | 922 +++++++++++ src/osmparser.c | 748 +++++++++ src/output.c | 816 +++++++++ src/planetsplitter.c | 410 +++++ src/profiles.c | 1018 ++++++++++++ src/profiles.h | 79 + src/queue.c | 201 +++ src/results.c | 242 +++ src/results.h | 112 ++ src/router.c | 814 +++++++++ src/segments.c | 170 ++ src/segments.h | 101 ++ src/segmentsx.c | 1053 ++++++++++++ src/segmentsx.h | 100 ++ src/sorting.c | 650 ++++++++ src/superx.c | 480 ++++++ src/superx.h | 38 + src/tagging.c | 574 +++++++ src/tagging.h | 95 ++ src/tagmodifier.c | 626 +++++++ src/translations.c | 1050 ++++++++++++ src/translations.h | 69 + src/types.c | 488 ++++++ src/types.h | 350 ++++ src/typesx.h | 56 + src/visualiser.c | 628 +++++++ src/visualiser.h | 48 + src/ways.c | 103 ++ src/ways.h | 96 ++ src/waysx.c | 623 +++++++ src/waysx.h | 87 + src/xml/Makefile | 134 ++ src/xml/test/bad-attr-character-ref.xml | 12 + src/xml/test/bad-attr-entity-ref.xml | 12 + src/xml/test/bad-cdata-start.xml | 13 + src/xml/test/bad-comment-ends-triple-dash.xml | 12 + src/xml/test/bad-double-quote-attr-amp.xml | 12 + src/xml/test/bad-double-quote-attr-left-angle.xml | 12 + src/xml/test/bad-double-quote-attr-right-angle.xml | 12 + src/xml/test/bad-early-end-of-file.xml | 11 + src/xml/test/bad-end-tag-space-at-begin1.xml | 12 + src/xml/test/bad-end-tag-space-at-begin2.xml | 12 + src/xml/test/bad-end-tag-space-at-end.xml | 12 + src/xml/test/bad-end-tag-with-attr.xml | 12 + src/xml/test/bad-single-quote-attr-amp.xml | 12 + src/xml/test/bad-single-quote-attr-left-angle.xml | 12 + src/xml/test/bad-single-quote-attr-right-angle.xml | 12 + src/xml/test/bad-start-tag-space-at-begin.xml | 12 + src/xml/test/bad-tag-attr-no-quotes.xml | 12 + src/xml/test/bad-tag-attr-space-after-equal.xml | 12 + src/xml/test/bad-tag-attr-space-before-equal.xml | 12 + src/xml/test/bad-tag-level-nesting.xml | 12 + src/xml/test/bad-unbalanced-tag-start-end.xml | 12 + src/xml/test/bad-unexpected-attribute-name.xml | 12 + src/xml/test/bad-unexpected-end-tag.xml | 12 + src/xml/test/bad-unexpected-left-angle.xml | 13 + src/xml/test/bad-unexpected-right-angle.xml | 13 + src/xml/test/bad-xml-header-at-begin.xml | 12 + src/xml/test/bad-xml-header-at-end.xml | 12 + src/xml/test/bad-xml-header-not-first.xml | 12 + src/xml/test/good.xml | 13 + src/xml/test/test.xsd | 39 + src/xml/xsd-to-xmlparser.c | 514 ++++++ src/xmlparse.h | 136 ++ src/xmlparse.l | 784 +++++++++ web/INSTALL.txt | 1 + web/data/create.sh | 30 + web/www/openlayers/install.sh | 25 + web/www/openlayers/routino.cfg | 44 + web/www/routino/.htaccess | 40 + web/www/routino/customrouter.cgi | 143 ++ web/www/routino/customvisualiser.cgi | 82 + web/www/routino/icons/ball-0.png | Bin 0 -> 128 bytes web/www/routino/icons/ball-1.png | Bin 0 -> 129 bytes web/www/routino/icons/ball-2.png | Bin 0 -> 133 bytes web/www/routino/icons/ball-3.png | Bin 0 -> 130 bytes web/www/routino/icons/ball-4.png | Bin 0 -> 136 bytes web/www/routino/icons/ball-5.png | Bin 0 -> 134 bytes web/www/routino/icons/ball-6.png | Bin 0 -> 134 bytes web/www/routino/icons/ball-7.png | Bin 0 -> 130 bytes web/www/routino/icons/ball-8.png | Bin 0 -> 130 bytes web/www/routino/icons/ball-9.png | Bin 0 -> 130 bytes web/www/routino/icons/create-icons.pl | 156 ++ web/www/routino/icons/home.png | Bin 0 -> 233 bytes web/www/routino/icons/limit-0.0.png | Bin 0 -> 649 bytes web/www/routino/icons/limit-0.png | Bin 0 -> 649 bytes web/www/routino/icons/limit-1.0.png | Bin 0 -> 667 bytes web/www/routino/icons/limit-1.1.png | Bin 0 -> 579 bytes web/www/routino/icons/limit-1.2.png | Bin 0 -> 668 bytes web/www/routino/icons/limit-1.3.png | Bin 0 -> 668 bytes web/www/routino/icons/limit-1.4.png | Bin 0 -> 664 bytes web/www/routino/icons/limit-1.5.png | Bin 0 -> 678 bytes web/www/routino/icons/limit-1.6.png | Bin 0 -> 673 bytes web/www/routino/icons/limit-1.7.png | Bin 0 -> 659 bytes web/www/routino/icons/limit-1.8.png | Bin 0 -> 676 bytes web/www/routino/icons/limit-1.9.png | Bin 0 -> 670 bytes web/www/routino/icons/limit-1.png | Bin 0 -> 534 bytes web/www/routino/icons/limit-10.0.png | Bin 0 -> 683 bytes web/www/routino/icons/limit-10.1.png | Bin 0 -> 676 bytes web/www/routino/icons/limit-10.2.png | Bin 0 -> 753 bytes web/www/routino/icons/limit-10.3.png | Bin 0 -> 755 bytes web/www/routino/icons/limit-10.4.png | Bin 0 -> 730 bytes web/www/routino/icons/limit-10.5.png | Bin 0 -> 764 bytes web/www/routino/icons/limit-10.6.png | Bin 0 -> 748 bytes web/www/routino/icons/limit-10.7.png | Bin 0 -> 754 bytes web/www/routino/icons/limit-10.8.png | Bin 0 -> 756 bytes web/www/routino/icons/limit-10.9.png | Bin 0 -> 752 bytes web/www/routino/icons/limit-10.png | Bin 0 -> 645 bytes web/www/routino/icons/limit-100.png | Bin 0 -> 708 bytes web/www/routino/icons/limit-101.png | Bin 0 -> 671 bytes web/www/routino/icons/limit-102.png | Bin 0 -> 728 bytes web/www/routino/icons/limit-103.png | Bin 0 -> 733 bytes web/www/routino/icons/limit-104.png | Bin 0 -> 733 bytes web/www/routino/icons/limit-105.png | Bin 0 -> 742 bytes web/www/routino/icons/limit-106.png | Bin 0 -> 741 bytes web/www/routino/icons/limit-107.png | Bin 0 -> 727 bytes web/www/routino/icons/limit-108.png | Bin 0 -> 738 bytes web/www/routino/icons/limit-109.png | Bin 0 -> 736 bytes web/www/routino/icons/limit-11.0.png | Bin 0 -> 673 bytes web/www/routino/icons/limit-11.1.png | Bin 0 -> 582 bytes web/www/routino/icons/limit-11.2.png | Bin 0 -> 671 bytes web/www/routino/icons/limit-11.3.png | Bin 0 -> 676 bytes web/www/routino/icons/limit-11.4.png | Bin 0 -> 653 bytes web/www/routino/icons/limit-11.5.png | Bin 0 -> 681 bytes web/www/routino/icons/limit-11.6.png | Bin 0 -> 680 bytes web/www/routino/icons/limit-11.7.png | Bin 0 -> 674 bytes web/www/routino/icons/limit-11.8.png | Bin 0 -> 680 bytes web/www/routino/icons/limit-11.9.png | Bin 0 -> 676 bytes web/www/routino/icons/limit-11.png | Bin 0 -> 552 bytes web/www/routino/icons/limit-110.png | Bin 0 -> 666 bytes web/www/routino/icons/limit-111.png | Bin 0 -> 545 bytes web/www/routino/icons/limit-112.png | Bin 0 -> 668 bytes web/www/routino/icons/limit-113.png | Bin 0 -> 671 bytes web/www/routino/icons/limit-114.png | Bin 0 -> 661 bytes web/www/routino/icons/limit-115.png | Bin 0 -> 673 bytes web/www/routino/icons/limit-116.png | Bin 0 -> 668 bytes web/www/routino/icons/limit-117.png | Bin 0 -> 653 bytes web/www/routino/icons/limit-118.png | Bin 0 -> 671 bytes web/www/routino/icons/limit-119.png | Bin 0 -> 673 bytes web/www/routino/icons/limit-12.0.png | Bin 0 -> 754 bytes web/www/routino/icons/limit-12.1.png | Bin 0 -> 688 bytes web/www/routino/icons/limit-12.2.png | Bin 0 -> 705 bytes web/www/routino/icons/limit-12.3.png | Bin 0 -> 760 bytes web/www/routino/icons/limit-12.4.png | Bin 0 -> 749 bytes web/www/routino/icons/limit-12.5.png | Bin 0 -> 761 bytes web/www/routino/icons/limit-12.6.png | Bin 0 -> 759 bytes web/www/routino/icons/limit-12.7.png | Bin 0 -> 755 bytes web/www/routino/icons/limit-12.8.png | Bin 0 -> 759 bytes web/www/routino/icons/limit-12.9.png | Bin 0 -> 754 bytes web/www/routino/icons/limit-12.png | Bin 0 -> 645 bytes web/www/routino/icons/limit-120.png | Bin 0 -> 735 bytes web/www/routino/icons/limit-121.png | Bin 0 -> 678 bytes web/www/routino/icons/limit-122.png | Bin 0 -> 721 bytes web/www/routino/icons/limit-123.png | Bin 0 -> 730 bytes web/www/routino/icons/limit-124.png | Bin 0 -> 733 bytes web/www/routino/icons/limit-125.png | Bin 0 -> 737 bytes web/www/routino/icons/limit-126.png | Bin 0 -> 733 bytes web/www/routino/icons/limit-127.png | Bin 0 -> 725 bytes web/www/routino/icons/limit-128.png | Bin 0 -> 732 bytes web/www/routino/icons/limit-129.png | Bin 0 -> 729 bytes web/www/routino/icons/limit-13.0.png | Bin 0 -> 748 bytes web/www/routino/icons/limit-13.1.png | Bin 0 -> 683 bytes web/www/routino/icons/limit-13.2.png | Bin 0 -> 753 bytes web/www/routino/icons/limit-13.3.png | Bin 0 -> 713 bytes web/www/routino/icons/limit-13.4.png | Bin 0 -> 731 bytes web/www/routino/icons/limit-13.5.png | Bin 0 -> 766 bytes web/www/routino/icons/limit-13.6.png | Bin 0 -> 764 bytes web/www/routino/icons/limit-13.7.png | Bin 0 -> 755 bytes web/www/routino/icons/limit-13.8.png | Bin 0 -> 762 bytes web/www/routino/icons/limit-13.9.png | Bin 0 -> 760 bytes web/www/routino/icons/limit-13.png | Bin 0 -> 647 bytes web/www/routino/icons/limit-130.png | Bin 0 -> 728 bytes web/www/routino/icons/limit-131.png | Bin 0 -> 678 bytes web/www/routino/icons/limit-132.png | Bin 0 -> 736 bytes web/www/routino/icons/limit-133.png | Bin 0 -> 701 bytes web/www/routino/icons/limit-134.png | Bin 0 -> 735 bytes web/www/routino/icons/limit-135.png | Bin 0 -> 737 bytes web/www/routino/icons/limit-136.png | Bin 0 -> 735 bytes web/www/routino/icons/limit-137.png | Bin 0 -> 722 bytes web/www/routino/icons/limit-138.png | Bin 0 -> 735 bytes web/www/routino/icons/limit-139.png | Bin 0 -> 735 bytes web/www/routino/icons/limit-14.0.png | Bin 0 -> 750 bytes web/www/routino/icons/limit-14.1.png | Bin 0 -> 668 bytes web/www/routino/icons/limit-14.2.png | Bin 0 -> 753 bytes web/www/routino/icons/limit-14.3.png | Bin 0 -> 751 bytes web/www/routino/icons/limit-14.4.png | Bin 0 -> 651 bytes web/www/routino/icons/limit-14.5.png | Bin 0 -> 756 bytes web/www/routino/icons/limit-14.6.png | Bin 0 -> 746 bytes web/www/routino/icons/limit-14.7.png | Bin 0 -> 751 bytes web/www/routino/icons/limit-14.8.png | Bin 0 -> 754 bytes web/www/routino/icons/limit-14.9.png | Bin 0 -> 748 bytes web/www/routino/icons/limit-14.png | Bin 0 -> 642 bytes web/www/routino/icons/limit-140.png | Bin 0 -> 728 bytes web/www/routino/icons/limit-141.png | Bin 0 -> 649 bytes web/www/routino/icons/limit-142.png | Bin 0 -> 728 bytes web/www/routino/icons/limit-143.png | Bin 0 -> 725 bytes web/www/routino/icons/limit-144.png | Bin 0 -> 662 bytes web/www/routino/icons/limit-145.png | Bin 0 -> 733 bytes web/www/routino/icons/limit-146.png | Bin 0 -> 731 bytes web/www/routino/icons/limit-147.png | Bin 0 -> 724 bytes web/www/routino/icons/limit-148.png | Bin 0 -> 732 bytes web/www/routino/icons/limit-149.png | Bin 0 -> 732 bytes web/www/routino/icons/limit-15.0.png | Bin 0 -> 758 bytes web/www/routino/icons/limit-15.1.png | Bin 0 -> 684 bytes web/www/routino/icons/limit-15.2.png | Bin 0 -> 757 bytes web/www/routino/icons/limit-15.3.png | Bin 0 -> 765 bytes web/www/routino/icons/limit-15.4.png | Bin 0 -> 735 bytes web/www/routino/icons/limit-15.5.png | Bin 0 -> 720 bytes web/www/routino/icons/limit-15.6.png | Bin 0 -> 757 bytes web/www/routino/icons/limit-15.7.png | Bin 0 -> 760 bytes web/www/routino/icons/limit-15.8.png | Bin 0 -> 765 bytes web/www/routino/icons/limit-15.9.png | Bin 0 -> 762 bytes web/www/routino/icons/limit-15.png | Bin 0 -> 662 bytes web/www/routino/icons/limit-150.png | Bin 0 -> 739 bytes web/www/routino/icons/limit-151.png | Bin 0 -> 675 bytes web/www/routino/icons/limit-152.png | Bin 0 -> 736 bytes web/www/routino/icons/limit-153.png | Bin 0 -> 741 bytes web/www/routino/icons/limit-154.png | Bin 0 -> 742 bytes web/www/routino/icons/limit-155.png | Bin 0 -> 724 bytes web/www/routino/icons/limit-156.png | Bin 0 -> 738 bytes web/www/routino/icons/limit-157.png | Bin 0 -> 716 bytes web/www/routino/icons/limit-158.png | Bin 0 -> 740 bytes web/www/routino/icons/limit-159.png | Bin 0 -> 738 bytes web/www/routino/icons/limit-16.0.png | Bin 0 -> 760 bytes web/www/routino/icons/limit-16.1.png | Bin 0 -> 682 bytes web/www/routino/icons/limit-16.2.png | Bin 0 -> 758 bytes web/www/routino/icons/limit-16.3.png | Bin 0 -> 761 bytes web/www/routino/icons/limit-16.4.png | Bin 0 -> 734 bytes web/www/routino/icons/limit-16.5.png | Bin 0 -> 767 bytes web/www/routino/icons/limit-16.6.png | Bin 0 -> 706 bytes web/www/routino/icons/limit-16.7.png | Bin 0 -> 761 bytes web/www/routino/icons/limit-16.8.png | Bin 0 -> 762 bytes web/www/routino/icons/limit-16.9.png | Bin 0 -> 758 bytes web/www/routino/icons/limit-16.png | Bin 0 -> 648 bytes web/www/routino/icons/limit-160.png | Bin 0 -> 741 bytes web/www/routino/icons/limit-17.0.png | Bin 0 -> 741 bytes web/www/routino/icons/limit-17.1.png | Bin 0 -> 673 bytes web/www/routino/icons/limit-17.2.png | Bin 0 -> 747 bytes web/www/routino/icons/limit-17.3.png | Bin 0 -> 744 bytes web/www/routino/icons/limit-17.4.png | Bin 0 -> 728 bytes web/www/routino/icons/limit-17.5.png | Bin 0 -> 750 bytes web/www/routino/icons/limit-17.6.png | Bin 0 -> 748 bytes web/www/routino/icons/limit-17.7.png | Bin 0 -> 668 bytes web/www/routino/icons/limit-17.8.png | Bin 0 -> 742 bytes web/www/routino/icons/limit-17.9.png | Bin 0 -> 743 bytes web/www/routino/icons/limit-17.png | Bin 0 -> 636 bytes web/www/routino/icons/limit-18.0.png | Bin 0 -> 756 bytes web/www/routino/icons/limit-18.1.png | Bin 0 -> 684 bytes web/www/routino/icons/limit-18.2.png | Bin 0 -> 757 bytes web/www/routino/icons/limit-18.3.png | Bin 0 -> 759 bytes web/www/routino/icons/limit-18.4.png | Bin 0 -> 735 bytes web/www/routino/icons/limit-18.5.png | Bin 0 -> 760 bytes web/www/routino/icons/limit-18.6.png | Bin 0 -> 761 bytes web/www/routino/icons/limit-18.7.png | Bin 0 -> 753 bytes web/www/routino/icons/limit-18.8.png | Bin 0 -> 709 bytes web/www/routino/icons/limit-18.9.png | Bin 0 -> 759 bytes web/www/routino/icons/limit-18.png | Bin 0 -> 647 bytes web/www/routino/icons/limit-19.0.png | Bin 0 -> 753 bytes web/www/routino/icons/limit-19.1.png | Bin 0 -> 681 bytes web/www/routino/icons/limit-19.2.png | Bin 0 -> 752 bytes web/www/routino/icons/limit-19.3.png | Bin 0 -> 759 bytes web/www/routino/icons/limit-19.4.png | Bin 0 -> 727 bytes web/www/routino/icons/limit-19.5.png | Bin 0 -> 756 bytes web/www/routino/icons/limit-19.6.png | Bin 0 -> 756 bytes web/www/routino/icons/limit-19.7.png | Bin 0 -> 757 bytes web/www/routino/icons/limit-19.8.png | Bin 0 -> 758 bytes web/www/routino/icons/limit-19.9.png | Bin 0 -> 691 bytes web/www/routino/icons/limit-19.png | Bin 0 -> 646 bytes web/www/routino/icons/limit-2.0.png | Bin 0 -> 713 bytes web/www/routino/icons/limit-2.1.png | Bin 0 -> 673 bytes web/www/routino/icons/limit-2.2.png | Bin 0 -> 644 bytes web/www/routino/icons/limit-2.3.png | Bin 0 -> 717 bytes web/www/routino/icons/limit-2.4.png | Bin 0 -> 705 bytes web/www/routino/icons/limit-2.5.png | Bin 0 -> 729 bytes web/www/routino/icons/limit-2.6.png | Bin 0 -> 713 bytes web/www/routino/icons/limit-2.7.png | Bin 0 -> 701 bytes web/www/routino/icons/limit-2.8.png | Bin 0 -> 712 bytes web/www/routino/icons/limit-2.9.png | Bin 0 -> 710 bytes web/www/routino/icons/limit-2.png | Bin 0 -> 582 bytes web/www/routino/icons/limit-20.0.png | Bin 0 -> 746 bytes web/www/routino/icons/limit-20.png | Bin 0 -> 683 bytes web/www/routino/icons/limit-21.png | Bin 0 -> 643 bytes web/www/routino/icons/limit-22.png | Bin 0 -> 669 bytes web/www/routino/icons/limit-23.png | Bin 0 -> 684 bytes web/www/routino/icons/limit-24.png | Bin 0 -> 678 bytes web/www/routino/icons/limit-25.png | Bin 0 -> 695 bytes web/www/routino/icons/limit-26.png | Bin 0 -> 684 bytes web/www/routino/icons/limit-27.png | Bin 0 -> 682 bytes web/www/routino/icons/limit-28.png | Bin 0 -> 685 bytes web/www/routino/icons/limit-29.png | Bin 0 -> 685 bytes web/www/routino/icons/limit-3.0.png | Bin 0 -> 707 bytes web/www/routino/icons/limit-3.1.png | Bin 0 -> 677 bytes web/www/routino/icons/limit-3.2.png | Bin 0 -> 718 bytes web/www/routino/icons/limit-3.3.png | Bin 0 -> 636 bytes web/www/routino/icons/limit-3.4.png | Bin 0 -> 690 bytes web/www/routino/icons/limit-3.5.png | Bin 0 -> 727 bytes web/www/routino/icons/limit-3.6.png | Bin 0 -> 716 bytes web/www/routino/icons/limit-3.7.png | Bin 0 -> 708 bytes web/www/routino/icons/limit-3.8.png | Bin 0 -> 720 bytes web/www/routino/icons/limit-3.9.png | Bin 0 -> 712 bytes web/www/routino/icons/limit-3.png | Bin 0 -> 580 bytes web/www/routino/icons/limit-30.png | Bin 0 -> 675 bytes web/www/routino/icons/limit-31.png | Bin 0 -> 643 bytes web/www/routino/icons/limit-32.png | Bin 0 -> 686 bytes web/www/routino/icons/limit-33.png | Bin 0 -> 651 bytes web/www/routino/icons/limit-34.png | Bin 0 -> 661 bytes web/www/routino/icons/limit-35.png | Bin 0 -> 688 bytes web/www/routino/icons/limit-36.png | Bin 0 -> 684 bytes web/www/routino/icons/limit-37.png | Bin 0 -> 677 bytes web/www/routino/icons/limit-38.png | Bin 0 -> 685 bytes web/www/routino/icons/limit-39.png | Bin 0 -> 684 bytes web/www/routino/icons/limit-4.0.png | Bin 0 -> 689 bytes web/www/routino/icons/limit-4.1.png | Bin 0 -> 666 bytes web/www/routino/icons/limit-4.2.png | Bin 0 -> 714 bytes web/www/routino/icons/limit-4.3.png | Bin 0 -> 695 bytes web/www/routino/icons/limit-4.4.png | Bin 0 -> 603 bytes web/www/routino/icons/limit-4.5.png | Bin 0 -> 700 bytes web/www/routino/icons/limit-4.6.png | Bin 0 -> 686 bytes web/www/routino/icons/limit-4.7.png | Bin 0 -> 672 bytes web/www/routino/icons/limit-4.8.png | Bin 0 -> 698 bytes web/www/routino/icons/limit-4.9.png | Bin 0 -> 704 bytes web/www/routino/icons/limit-4.png | Bin 0 -> 555 bytes web/www/routino/icons/limit-40.png | Bin 0 -> 660 bytes web/www/routino/icons/limit-41.png | Bin 0 -> 633 bytes web/www/routino/icons/limit-42.png | Bin 0 -> 678 bytes web/www/routino/icons/limit-43.png | Bin 0 -> 655 bytes web/www/routino/icons/limit-44.png | Bin 0 -> 600 bytes web/www/routino/icons/limit-45.png | Bin 0 -> 680 bytes web/www/routino/icons/limit-46.png | Bin 0 -> 654 bytes web/www/routino/icons/limit-47.png | Bin 0 -> 644 bytes web/www/routino/icons/limit-48.png | Bin 0 -> 656 bytes web/www/routino/icons/limit-49.png | Bin 0 -> 659 bytes web/www/routino/icons/limit-5.0.png | Bin 0 -> 713 bytes web/www/routino/icons/limit-5.1.png | Bin 0 -> 680 bytes web/www/routino/icons/limit-5.2.png | Bin 0 -> 725 bytes web/www/routino/icons/limit-5.3.png | Bin 0 -> 724 bytes web/www/routino/icons/limit-5.4.png | Bin 0 -> 695 bytes web/www/routino/icons/limit-5.5.png | Bin 0 -> 627 bytes web/www/routino/icons/limit-5.6.png | Bin 0 -> 720 bytes web/www/routino/icons/limit-5.7.png | Bin 0 -> 692 bytes web/www/routino/icons/limit-5.8.png | Bin 0 -> 725 bytes web/www/routino/icons/limit-5.9.png | Bin 0 -> 716 bytes web/www/routino/icons/limit-5.png | Bin 0 -> 587 bytes web/www/routino/icons/limit-50.png | Bin 0 -> 688 bytes web/www/routino/icons/limit-51.png | Bin 0 -> 652 bytes web/www/routino/icons/limit-52.png | Bin 0 -> 689 bytes web/www/routino/icons/limit-53.png | Bin 0 -> 692 bytes web/www/routino/icons/limit-54.png | Bin 0 -> 669 bytes web/www/routino/icons/limit-55.png | Bin 0 -> 655 bytes web/www/routino/icons/limit-56.png | Bin 0 -> 690 bytes web/www/routino/icons/limit-57.png | Bin 0 -> 661 bytes web/www/routino/icons/limit-58.png | Bin 0 -> 688 bytes web/www/routino/icons/limit-59.png | Bin 0 -> 690 bytes web/www/routino/icons/limit-6.0.png | Bin 0 -> 716 bytes web/www/routino/icons/limit-6.1.png | Bin 0 -> 675 bytes web/www/routino/icons/limit-6.2.png | Bin 0 -> 712 bytes web/www/routino/icons/limit-6.3.png | Bin 0 -> 721 bytes web/www/routino/icons/limit-6.4.png | Bin 0 -> 687 bytes web/www/routino/icons/limit-6.5.png | Bin 0 -> 726 bytes web/www/routino/icons/limit-6.6.png | Bin 0 -> 636 bytes web/www/routino/icons/limit-6.7.png | Bin 0 -> 711 bytes web/www/routino/icons/limit-6.8.png | Bin 0 -> 722 bytes web/www/routino/icons/limit-6.9.png | Bin 0 -> 713 bytes web/www/routino/icons/limit-6.png | Bin 0 -> 580 bytes web/www/routino/icons/limit-60.png | Bin 0 -> 682 bytes web/www/routino/icons/limit-61.png | Bin 0 -> 642 bytes web/www/routino/icons/limit-62.png | Bin 0 -> 684 bytes web/www/routino/icons/limit-63.png | Bin 0 -> 680 bytes web/www/routino/icons/limit-64.png | Bin 0 -> 664 bytes web/www/routino/icons/limit-65.png | Bin 0 -> 690 bytes web/www/routino/icons/limit-66.png | Bin 0 -> 653 bytes web/www/routino/icons/limit-67.png | Bin 0 -> 682 bytes web/www/routino/icons/limit-68.png | Bin 0 -> 687 bytes web/www/routino/icons/limit-69.png | Bin 0 -> 684 bytes web/www/routino/icons/limit-7.0.png | Bin 0 -> 687 bytes web/www/routino/icons/limit-7.1.png | Bin 0 -> 653 bytes web/www/routino/icons/limit-7.2.png | Bin 0 -> 700 bytes web/www/routino/icons/limit-7.3.png | Bin 0 -> 708 bytes web/www/routino/icons/limit-7.4.png | Bin 0 -> 660 bytes web/www/routino/icons/limit-7.5.png | Bin 0 -> 696 bytes web/www/routino/icons/limit-7.6.png | Bin 0 -> 698 bytes web/www/routino/icons/limit-7.7.png | Bin 0 -> 583 bytes web/www/routino/icons/limit-7.8.png | Bin 0 -> 708 bytes web/www/routino/icons/limit-7.9.png | Bin 0 -> 702 bytes web/www/routino/icons/limit-7.png | Bin 0 -> 565 bytes web/www/routino/icons/limit-70.png | Bin 0 -> 670 bytes web/www/routino/icons/limit-71.png | Bin 0 -> 628 bytes web/www/routino/icons/limit-72.png | Bin 0 -> 674 bytes web/www/routino/icons/limit-73.png | Bin 0 -> 678 bytes web/www/routino/icons/limit-74.png | Bin 0 -> 652 bytes web/www/routino/icons/limit-75.png | Bin 0 -> 655 bytes web/www/routino/icons/limit-76.png | Bin 0 -> 675 bytes web/www/routino/icons/limit-77.png | Bin 0 -> 574 bytes web/www/routino/icons/limit-78.png | Bin 0 -> 675 bytes web/www/routino/icons/limit-79.png | Bin 0 -> 678 bytes web/www/routino/icons/limit-8.0.png | Bin 0 -> 708 bytes web/www/routino/icons/limit-8.1.png | Bin 0 -> 676 bytes web/www/routino/icons/limit-8.2.png | Bin 0 -> 707 bytes web/www/routino/icons/limit-8.3.png | Bin 0 -> 719 bytes web/www/routino/icons/limit-8.4.png | Bin 0 -> 688 bytes web/www/routino/icons/limit-8.5.png | Bin 0 -> 722 bytes web/www/routino/icons/limit-8.6.png | Bin 0 -> 713 bytes web/www/routino/icons/limit-8.7.png | Bin 0 -> 704 bytes web/www/routino/icons/limit-8.8.png | Bin 0 -> 637 bytes web/www/routino/icons/limit-8.9.png | Bin 0 -> 716 bytes web/www/routino/icons/limit-8.png | Bin 0 -> 581 bytes web/www/routino/icons/limit-80.png | Bin 0 -> 678 bytes web/www/routino/icons/limit-81.png | Bin 0 -> 645 bytes web/www/routino/icons/limit-82.png | Bin 0 -> 681 bytes web/www/routino/icons/limit-83.png | Bin 0 -> 678 bytes web/www/routino/icons/limit-84.png | Bin 0 -> 657 bytes web/www/routino/icons/limit-85.png | Bin 0 -> 685 bytes web/www/routino/icons/limit-86.png | Bin 0 -> 682 bytes web/www/routino/icons/limit-87.png | Bin 0 -> 673 bytes web/www/routino/icons/limit-88.png | Bin 0 -> 656 bytes web/www/routino/icons/limit-89.png | Bin 0 -> 684 bytes web/www/routino/icons/limit-9.0.png | Bin 0 -> 699 bytes web/www/routino/icons/limit-9.1.png | Bin 0 -> 673 bytes web/www/routino/icons/limit-9.2.png | Bin 0 -> 710 bytes web/www/routino/icons/limit-9.3.png | Bin 0 -> 718 bytes web/www/routino/icons/limit-9.4.png | Bin 0 -> 697 bytes web/www/routino/icons/limit-9.5.png | Bin 0 -> 715 bytes web/www/routino/icons/limit-9.6.png | Bin 0 -> 705 bytes web/www/routino/icons/limit-9.7.png | Bin 0 -> 700 bytes web/www/routino/icons/limit-9.8.png | Bin 0 -> 717 bytes web/www/routino/icons/limit-9.9.png | Bin 0 -> 628 bytes web/www/routino/icons/limit-9.png | Bin 0 -> 578 bytes web/www/routino/icons/limit-90.png | Bin 0 -> 674 bytes web/www/routino/icons/limit-91.png | Bin 0 -> 640 bytes web/www/routino/icons/limit-92.png | Bin 0 -> 682 bytes web/www/routino/icons/limit-93.png | Bin 0 -> 677 bytes web/www/routino/icons/limit-94.png | Bin 0 -> 677 bytes web/www/routino/icons/limit-95.png | Bin 0 -> 690 bytes web/www/routino/icons/limit-96.png | Bin 0 -> 684 bytes web/www/routino/icons/limit-97.png | Bin 0 -> 677 bytes web/www/routino/icons/limit-98.png | Bin 0 -> 689 bytes web/www/routino/icons/limit-99.png | Bin 0 -> 644 bytes web/www/routino/icons/limit-no.png | Bin 0 -> 649 bytes web/www/routino/icons/marker-0-grey.png | Bin 0 -> 1095 bytes web/www/routino/icons/marker-0-red.png | Bin 0 -> 990 bytes web/www/routino/icons/marker-1-grey.png | Bin 0 -> 963 bytes web/www/routino/icons/marker-1-red.png | Bin 0 -> 902 bytes web/www/routino/icons/marker-2-grey.png | Bin 0 -> 1091 bytes web/www/routino/icons/marker-2-red.png | Bin 0 -> 997 bytes web/www/routino/icons/marker-3-grey.png | Bin 0 -> 1110 bytes web/www/routino/icons/marker-3-red.png | Bin 0 -> 1007 bytes web/www/routino/icons/marker-4-grey.png | Bin 0 -> 1029 bytes web/www/routino/icons/marker-4-red.png | Bin 0 -> 950 bytes web/www/routino/icons/marker-5-grey.png | Bin 0 -> 1099 bytes web/www/routino/icons/marker-5-red.png | Bin 0 -> 1002 bytes web/www/routino/icons/marker-6-grey.png | Bin 0 -> 1085 bytes web/www/routino/icons/marker-6-red.png | Bin 0 -> 1008 bytes web/www/routino/icons/marker-7-grey.png | Bin 0 -> 1046 bytes web/www/routino/icons/marker-7-red.png | Bin 0 -> 954 bytes web/www/routino/icons/marker-8-grey.png | Bin 0 -> 1100 bytes web/www/routino/icons/marker-8-red.png | Bin 0 -> 1016 bytes web/www/routino/icons/marker-9-grey.png | Bin 0 -> 1112 bytes web/www/routino/icons/marker-9-red.png | Bin 0 -> 1011 bytes web/www/routino/icons/marker-home-grey.png | Bin 0 -> 1148 bytes web/www/routino/icons/marker-home-red.png | Bin 0 -> 1049 bytes web/www/routino/icons/waypoint-add.png | Bin 0 -> 163 bytes web/www/routino/icons/waypoint-centre.png | Bin 0 -> 211 bytes web/www/routino/icons/waypoint-down.png | Bin 0 -> 201 bytes web/www/routino/icons/waypoint-home.png | Bin 0 -> 243 bytes web/www/routino/icons/waypoint-remove.png | Bin 0 -> 241 bytes web/www/routino/icons/waypoint-up.png | Bin 0 -> 201 bytes web/www/routino/index.html | 70 + web/www/routino/maplayout-ie6-bugfixes.css | 83 + web/www/routino/maplayout-ie7-bugfixes.css | 64 + web/www/routino/maplayout.css | 101 ++ web/www/routino/noscript.cgi | 196 +++ web/www/routino/noscript.html | 70 + web/www/routino/noscript.template.html | 420 +++++ web/www/routino/page-elements.css | 143 ++ web/www/routino/page-elements.js | 91 + web/www/routino/paths.pl | 32 + web/www/routino/results.cgi | 74 + web/www/routino/router.cgi | 105 ++ web/www/routino/router.css | 171 ++ web/www/routino/router.html | 1 + web/www/routino/router.html.en | 550 +++++++ web/www/routino/router.js | 1503 +++++++++++++++++ web/www/routino/router.pl | 249 +++ web/www/routino/statistics.cgi | 39 + web/www/routino/visualiser.cgi | 110 ++ web/www/routino/visualiser.css | 49 + web/www/routino/visualiser.html | 258 +++ web/www/routino/visualiser.js | 618 +++++++ xml/Makefile | 48 + xml/osm.xsd | 112 ++ xml/routino-osm.xsd | 123 ++ xml/routino-profiles.xml | 461 ++++++ xml/routino-profiles.xsd | 108 ++ xml/routino-tagging-nomodify.xml | 43 + xml/routino-tagging.xml | 435 +++++ xml/routino-tagging.xsd | 67 + xml/routino-translations.xml | 177 ++ xml/routino-translations.xsd | 171 ++ xml/xsd.xsd | 65 + 536 files changed, 32476 insertions(+) create mode 100644 ChangeLog create mode 120000 INSTALL.txt create mode 100644 Makefile create mode 120000 NEWS.txt create mode 120000 README.txt create mode 100644 agpl-3.0.txt create mode 100644 doc/ALGORITHM.txt create mode 100644 doc/CONFIGURATION.txt create mode 100644 doc/DATA.txt create mode 100644 doc/INSTALL.txt create mode 100644 doc/Makefile create mode 100644 doc/NEWS.txt create mode 100644 doc/OUTPUT.txt create mode 100644 doc/README.txt create mode 100644 doc/TAGGING.txt create mode 100644 doc/USAGE.txt create mode 100644 doc/html/algorithm.html create mode 100644 doc/html/configuration.html create mode 100644 doc/html/data.html create mode 100644 doc/html/example0.png create mode 100644 doc/html/example1.png create mode 100644 doc/html/example2.png create mode 100644 doc/html/index.html create mode 100644 doc/html/installation.html create mode 100644 doc/html/output.html create mode 100644 doc/html/style.css create mode 100644 doc/html/tagging.html create mode 100644 doc/html/usage.html create mode 100644 src/Makefile create mode 100644 src/filedumper.c create mode 100644 src/files.c create mode 100644 src/functions.h create mode 100644 src/functionsx.h create mode 100644 src/nodes.c create mode 100644 src/nodes.h create mode 100644 src/nodesx.c create mode 100644 src/nodesx.h create mode 100644 src/optimiser.c create mode 100644 src/osmparser.c create mode 100644 src/output.c create mode 100644 src/planetsplitter.c create mode 100644 src/profiles.c create mode 100644 src/profiles.h create mode 100644 src/queue.c create mode 100644 src/results.c create mode 100644 src/results.h create mode 100644 src/router.c create mode 100644 src/segments.c create mode 100644 src/segments.h create mode 100644 src/segmentsx.c create mode 100644 src/segmentsx.h create mode 100644 src/sorting.c create mode 100644 src/superx.c create mode 100644 src/superx.h create mode 100644 src/tagging.c create mode 100644 src/tagging.h create mode 100644 src/tagmodifier.c create mode 100644 src/translations.c create mode 100644 src/translations.h create mode 100644 src/types.c create mode 100644 src/types.h create mode 100644 src/typesx.h create mode 100644 src/visualiser.c create mode 100644 src/visualiser.h create mode 100644 src/ways.c create mode 100644 src/ways.h create mode 100644 src/waysx.c create mode 100644 src/waysx.h create mode 100644 src/xml/Makefile create mode 100644 src/xml/test/bad-attr-character-ref.xml create mode 100644 src/xml/test/bad-attr-entity-ref.xml create mode 100644 src/xml/test/bad-cdata-start.xml create mode 100644 src/xml/test/bad-comment-ends-triple-dash.xml create mode 100644 src/xml/test/bad-double-quote-attr-amp.xml create mode 100644 src/xml/test/bad-double-quote-attr-left-angle.xml create mode 100644 src/xml/test/bad-double-quote-attr-right-angle.xml create mode 100644 src/xml/test/bad-early-end-of-file.xml create mode 100644 src/xml/test/bad-end-tag-space-at-begin1.xml create mode 100644 src/xml/test/bad-end-tag-space-at-begin2.xml create mode 100644 src/xml/test/bad-end-tag-space-at-end.xml create mode 100644 src/xml/test/bad-end-tag-with-attr.xml create mode 100644 src/xml/test/bad-single-quote-attr-amp.xml create mode 100644 src/xml/test/bad-single-quote-attr-left-angle.xml create mode 100644 src/xml/test/bad-single-quote-attr-right-angle.xml create mode 100644 src/xml/test/bad-start-tag-space-at-begin.xml create mode 100644 src/xml/test/bad-tag-attr-no-quotes.xml create mode 100644 src/xml/test/bad-tag-attr-space-after-equal.xml create mode 100644 src/xml/test/bad-tag-attr-space-before-equal.xml create mode 100644 src/xml/test/bad-tag-level-nesting.xml create mode 100644 src/xml/test/bad-unbalanced-tag-start-end.xml create mode 100644 src/xml/test/bad-unexpected-attribute-name.xml create mode 100644 src/xml/test/bad-unexpected-end-tag.xml create mode 100644 src/xml/test/bad-unexpected-left-angle.xml create mode 100644 src/xml/test/bad-unexpected-right-angle.xml create mode 100644 src/xml/test/bad-xml-header-at-begin.xml create mode 100644 src/xml/test/bad-xml-header-at-end.xml create mode 100644 src/xml/test/bad-xml-header-not-first.xml create mode 100644 src/xml/test/good.xml create mode 100644 src/xml/test/test.xsd create mode 100644 src/xml/xsd-to-xmlparser.c create mode 100644 src/xmlparse.h create mode 100644 src/xmlparse.l create mode 120000 web/INSTALL.txt create mode 100755 web/data/create.sh create mode 100755 web/www/openlayers/install.sh create mode 100644 web/www/openlayers/routino.cfg create mode 100644 web/www/routino/.htaccess create mode 100755 web/www/routino/customrouter.cgi create mode 100755 web/www/routino/customvisualiser.cgi create mode 100644 web/www/routino/icons/ball-0.png create mode 100644 web/www/routino/icons/ball-1.png create mode 100644 web/www/routino/icons/ball-2.png create mode 100644 web/www/routino/icons/ball-3.png create mode 100644 web/www/routino/icons/ball-4.png create mode 100644 web/www/routino/icons/ball-5.png create mode 100644 web/www/routino/icons/ball-6.png create mode 100644 web/www/routino/icons/ball-7.png create mode 100644 web/www/routino/icons/ball-8.png create mode 100644 web/www/routino/icons/ball-9.png create mode 100755 web/www/routino/icons/create-icons.pl create mode 100644 web/www/routino/icons/home.png create mode 100644 web/www/routino/icons/limit-0.0.png create mode 100644 web/www/routino/icons/limit-0.png create mode 100644 web/www/routino/icons/limit-1.0.png create mode 100644 web/www/routino/icons/limit-1.1.png create mode 100644 web/www/routino/icons/limit-1.2.png create mode 100644 web/www/routino/icons/limit-1.3.png create mode 100644 web/www/routino/icons/limit-1.4.png create mode 100644 web/www/routino/icons/limit-1.5.png create mode 100644 web/www/routino/icons/limit-1.6.png create mode 100644 web/www/routino/icons/limit-1.7.png create mode 100644 web/www/routino/icons/limit-1.8.png create mode 100644 web/www/routino/icons/limit-1.9.png create mode 100644 web/www/routino/icons/limit-1.png create mode 100644 web/www/routino/icons/limit-10.0.png create mode 100644 web/www/routino/icons/limit-10.1.png create mode 100644 web/www/routino/icons/limit-10.2.png create mode 100644 web/www/routino/icons/limit-10.3.png create mode 100644 web/www/routino/icons/limit-10.4.png create mode 100644 web/www/routino/icons/limit-10.5.png create mode 100644 web/www/routino/icons/limit-10.6.png create mode 100644 web/www/routino/icons/limit-10.7.png create mode 100644 web/www/routino/icons/limit-10.8.png create mode 100644 web/www/routino/icons/limit-10.9.png create mode 100644 web/www/routino/icons/limit-10.png create mode 100644 web/www/routino/icons/limit-100.png create mode 100644 web/www/routino/icons/limit-101.png create mode 100644 web/www/routino/icons/limit-102.png create mode 100644 web/www/routino/icons/limit-103.png create mode 100644 web/www/routino/icons/limit-104.png create mode 100644 web/www/routino/icons/limit-105.png create mode 100644 web/www/routino/icons/limit-106.png create mode 100644 web/www/routino/icons/limit-107.png create mode 100644 web/www/routino/icons/limit-108.png create mode 100644 web/www/routino/icons/limit-109.png create mode 100644 web/www/routino/icons/limit-11.0.png create mode 100644 web/www/routino/icons/limit-11.1.png create mode 100644 web/www/routino/icons/limit-11.2.png create mode 100644 web/www/routino/icons/limit-11.3.png create mode 100644 web/www/routino/icons/limit-11.4.png create mode 100644 web/www/routino/icons/limit-11.5.png create mode 100644 web/www/routino/icons/limit-11.6.png create mode 100644 web/www/routino/icons/limit-11.7.png create mode 100644 web/www/routino/icons/limit-11.8.png create mode 100644 web/www/routino/icons/limit-11.9.png create mode 100644 web/www/routino/icons/limit-11.png create mode 100644 web/www/routino/icons/limit-110.png create mode 100644 web/www/routino/icons/limit-111.png create mode 100644 web/www/routino/icons/limit-112.png create mode 100644 web/www/routino/icons/limit-113.png create mode 100644 web/www/routino/icons/limit-114.png create mode 100644 web/www/routino/icons/limit-115.png create mode 100644 web/www/routino/icons/limit-116.png create mode 100644 web/www/routino/icons/limit-117.png create mode 100644 web/www/routino/icons/limit-118.png create mode 100644 web/www/routino/icons/limit-119.png create mode 100644 web/www/routino/icons/limit-12.0.png create mode 100644 web/www/routino/icons/limit-12.1.png create mode 100644 web/www/routino/icons/limit-12.2.png create mode 100644 web/www/routino/icons/limit-12.3.png create mode 100644 web/www/routino/icons/limit-12.4.png create mode 100644 web/www/routino/icons/limit-12.5.png create mode 100644 web/www/routino/icons/limit-12.6.png create mode 100644 web/www/routino/icons/limit-12.7.png create mode 100644 web/www/routino/icons/limit-12.8.png create mode 100644 web/www/routino/icons/limit-12.9.png create mode 100644 web/www/routino/icons/limit-12.png create mode 100644 web/www/routino/icons/limit-120.png create mode 100644 web/www/routino/icons/limit-121.png create mode 100644 web/www/routino/icons/limit-122.png create mode 100644 web/www/routino/icons/limit-123.png create mode 100644 web/www/routino/icons/limit-124.png create mode 100644 web/www/routino/icons/limit-125.png create mode 100644 web/www/routino/icons/limit-126.png create mode 100644 web/www/routino/icons/limit-127.png create mode 100644 web/www/routino/icons/limit-128.png create mode 100644 web/www/routino/icons/limit-129.png create mode 100644 web/www/routino/icons/limit-13.0.png create mode 100644 web/www/routino/icons/limit-13.1.png create mode 100644 web/www/routino/icons/limit-13.2.png create mode 100644 web/www/routino/icons/limit-13.3.png create mode 100644 web/www/routino/icons/limit-13.4.png create mode 100644 web/www/routino/icons/limit-13.5.png create mode 100644 web/www/routino/icons/limit-13.6.png create mode 100644 web/www/routino/icons/limit-13.7.png create mode 100644 web/www/routino/icons/limit-13.8.png create mode 100644 web/www/routino/icons/limit-13.9.png create mode 100644 web/www/routino/icons/limit-13.png create mode 100644 web/www/routino/icons/limit-130.png create mode 100644 web/www/routino/icons/limit-131.png create mode 100644 web/www/routino/icons/limit-132.png create mode 100644 web/www/routino/icons/limit-133.png create mode 100644 web/www/routino/icons/limit-134.png create mode 100644 web/www/routino/icons/limit-135.png create mode 100644 web/www/routino/icons/limit-136.png create mode 100644 web/www/routino/icons/limit-137.png create mode 100644 web/www/routino/icons/limit-138.png create mode 100644 web/www/routino/icons/limit-139.png create mode 100644 web/www/routino/icons/limit-14.0.png create mode 100644 web/www/routino/icons/limit-14.1.png create mode 100644 web/www/routino/icons/limit-14.2.png create mode 100644 web/www/routino/icons/limit-14.3.png create mode 100644 web/www/routino/icons/limit-14.4.png create mode 100644 web/www/routino/icons/limit-14.5.png create mode 100644 web/www/routino/icons/limit-14.6.png create mode 100644 web/www/routino/icons/limit-14.7.png create mode 100644 web/www/routino/icons/limit-14.8.png create mode 100644 web/www/routino/icons/limit-14.9.png create mode 100644 web/www/routino/icons/limit-14.png create mode 100644 web/www/routino/icons/limit-140.png create mode 100644 web/www/routino/icons/limit-141.png create mode 100644 web/www/routino/icons/limit-142.png create mode 100644 web/www/routino/icons/limit-143.png create mode 100644 web/www/routino/icons/limit-144.png create mode 100644 web/www/routino/icons/limit-145.png create mode 100644 web/www/routino/icons/limit-146.png create mode 100644 web/www/routino/icons/limit-147.png create mode 100644 web/www/routino/icons/limit-148.png create mode 100644 web/www/routino/icons/limit-149.png create mode 100644 web/www/routino/icons/limit-15.0.png create mode 100644 web/www/routino/icons/limit-15.1.png create mode 100644 web/www/routino/icons/limit-15.2.png create mode 100644 web/www/routino/icons/limit-15.3.png create mode 100644 web/www/routino/icons/limit-15.4.png create mode 100644 web/www/routino/icons/limit-15.5.png create mode 100644 web/www/routino/icons/limit-15.6.png create mode 100644 web/www/routino/icons/limit-15.7.png create mode 100644 web/www/routino/icons/limit-15.8.png create mode 100644 web/www/routino/icons/limit-15.9.png create mode 100644 web/www/routino/icons/limit-15.png create mode 100644 web/www/routino/icons/limit-150.png create mode 100644 web/www/routino/icons/limit-151.png create mode 100644 web/www/routino/icons/limit-152.png create mode 100644 web/www/routino/icons/limit-153.png create mode 100644 web/www/routino/icons/limit-154.png create mode 100644 web/www/routino/icons/limit-155.png create mode 100644 web/www/routino/icons/limit-156.png create mode 100644 web/www/routino/icons/limit-157.png create mode 100644 web/www/routino/icons/limit-158.png create mode 100644 web/www/routino/icons/limit-159.png create mode 100644 web/www/routino/icons/limit-16.0.png create mode 100644 web/www/routino/icons/limit-16.1.png create mode 100644 web/www/routino/icons/limit-16.2.png create mode 100644 web/www/routino/icons/limit-16.3.png create mode 100644 web/www/routino/icons/limit-16.4.png create mode 100644 web/www/routino/icons/limit-16.5.png create mode 100644 web/www/routino/icons/limit-16.6.png create mode 100644 web/www/routino/icons/limit-16.7.png create mode 100644 web/www/routino/icons/limit-16.8.png create mode 100644 web/www/routino/icons/limit-16.9.png create mode 100644 web/www/routino/icons/limit-16.png create mode 100644 web/www/routino/icons/limit-160.png create mode 100644 web/www/routino/icons/limit-17.0.png create mode 100644 web/www/routino/icons/limit-17.1.png create mode 100644 web/www/routino/icons/limit-17.2.png create mode 100644 web/www/routino/icons/limit-17.3.png create mode 100644 web/www/routino/icons/limit-17.4.png create mode 100644 web/www/routino/icons/limit-17.5.png create mode 100644 web/www/routino/icons/limit-17.6.png create mode 100644 web/www/routino/icons/limit-17.7.png create mode 100644 web/www/routino/icons/limit-17.8.png create mode 100644 web/www/routino/icons/limit-17.9.png create mode 100644 web/www/routino/icons/limit-17.png create mode 100644 web/www/routino/icons/limit-18.0.png create mode 100644 web/www/routino/icons/limit-18.1.png create mode 100644 web/www/routino/icons/limit-18.2.png create mode 100644 web/www/routino/icons/limit-18.3.png create mode 100644 web/www/routino/icons/limit-18.4.png create mode 100644 web/www/routino/icons/limit-18.5.png create mode 100644 web/www/routino/icons/limit-18.6.png create mode 100644 web/www/routino/icons/limit-18.7.png create mode 100644 web/www/routino/icons/limit-18.8.png create mode 100644 web/www/routino/icons/limit-18.9.png create mode 100644 web/www/routino/icons/limit-18.png create mode 100644 web/www/routino/icons/limit-19.0.png create mode 100644 web/www/routino/icons/limit-19.1.png create mode 100644 web/www/routino/icons/limit-19.2.png create mode 100644 web/www/routino/icons/limit-19.3.png create mode 100644 web/www/routino/icons/limit-19.4.png create mode 100644 web/www/routino/icons/limit-19.5.png create mode 100644 web/www/routino/icons/limit-19.6.png create mode 100644 web/www/routino/icons/limit-19.7.png create mode 100644 web/www/routino/icons/limit-19.8.png create mode 100644 web/www/routino/icons/limit-19.9.png create mode 100644 web/www/routino/icons/limit-19.png create mode 100644 web/www/routino/icons/limit-2.0.png create mode 100644 web/www/routino/icons/limit-2.1.png create mode 100644 web/www/routino/icons/limit-2.2.png create mode 100644 web/www/routino/icons/limit-2.3.png create mode 100644 web/www/routino/icons/limit-2.4.png create mode 100644 web/www/routino/icons/limit-2.5.png create mode 100644 web/www/routino/icons/limit-2.6.png create mode 100644 web/www/routino/icons/limit-2.7.png create mode 100644 web/www/routino/icons/limit-2.8.png create mode 100644 web/www/routino/icons/limit-2.9.png create mode 100644 web/www/routino/icons/limit-2.png create mode 100644 web/www/routino/icons/limit-20.0.png create mode 100644 web/www/routino/icons/limit-20.png create mode 100644 web/www/routino/icons/limit-21.png create mode 100644 web/www/routino/icons/limit-22.png create mode 100644 web/www/routino/icons/limit-23.png create mode 100644 web/www/routino/icons/limit-24.png create mode 100644 web/www/routino/icons/limit-25.png create mode 100644 web/www/routino/icons/limit-26.png create mode 100644 web/www/routino/icons/limit-27.png create mode 100644 web/www/routino/icons/limit-28.png create mode 100644 web/www/routino/icons/limit-29.png create mode 100644 web/www/routino/icons/limit-3.0.png create mode 100644 web/www/routino/icons/limit-3.1.png create mode 100644 web/www/routino/icons/limit-3.2.png create mode 100644 web/www/routino/icons/limit-3.3.png create mode 100644 web/www/routino/icons/limit-3.4.png create mode 100644 web/www/routino/icons/limit-3.5.png create mode 100644 web/www/routino/icons/limit-3.6.png create mode 100644 web/www/routino/icons/limit-3.7.png create mode 100644 web/www/routino/icons/limit-3.8.png create mode 100644 web/www/routino/icons/limit-3.9.png create mode 100644 web/www/routino/icons/limit-3.png create mode 100644 web/www/routino/icons/limit-30.png create mode 100644 web/www/routino/icons/limit-31.png create mode 100644 web/www/routino/icons/limit-32.png create mode 100644 web/www/routino/icons/limit-33.png create mode 100644 web/www/routino/icons/limit-34.png create mode 100644 web/www/routino/icons/limit-35.png create mode 100644 web/www/routino/icons/limit-36.png create mode 100644 web/www/routino/icons/limit-37.png create mode 100644 web/www/routino/icons/limit-38.png create mode 100644 web/www/routino/icons/limit-39.png create mode 100644 web/www/routino/icons/limit-4.0.png create mode 100644 web/www/routino/icons/limit-4.1.png create mode 100644 web/www/routino/icons/limit-4.2.png create mode 100644 web/www/routino/icons/limit-4.3.png create mode 100644 web/www/routino/icons/limit-4.4.png create mode 100644 web/www/routino/icons/limit-4.5.png create mode 100644 web/www/routino/icons/limit-4.6.png create mode 100644 web/www/routino/icons/limit-4.7.png create mode 100644 web/www/routino/icons/limit-4.8.png create mode 100644 web/www/routino/icons/limit-4.9.png create mode 100644 web/www/routino/icons/limit-4.png create mode 100644 web/www/routino/icons/limit-40.png create mode 100644 web/www/routino/icons/limit-41.png create mode 100644 web/www/routino/icons/limit-42.png create mode 100644 web/www/routino/icons/limit-43.png create mode 100644 web/www/routino/icons/limit-44.png create mode 100644 web/www/routino/icons/limit-45.png create mode 100644 web/www/routino/icons/limit-46.png create mode 100644 web/www/routino/icons/limit-47.png create mode 100644 web/www/routino/icons/limit-48.png create mode 100644 web/www/routino/icons/limit-49.png create mode 100644 web/www/routino/icons/limit-5.0.png create mode 100644 web/www/routino/icons/limit-5.1.png create mode 100644 web/www/routino/icons/limit-5.2.png create mode 100644 web/www/routino/icons/limit-5.3.png create mode 100644 web/www/routino/icons/limit-5.4.png create mode 100644 web/www/routino/icons/limit-5.5.png create mode 100644 web/www/routino/icons/limit-5.6.png create mode 100644 web/www/routino/icons/limit-5.7.png create mode 100644 web/www/routino/icons/limit-5.8.png create mode 100644 web/www/routino/icons/limit-5.9.png create mode 100644 web/www/routino/icons/limit-5.png create mode 100644 web/www/routino/icons/limit-50.png create mode 100644 web/www/routino/icons/limit-51.png create mode 100644 web/www/routino/icons/limit-52.png create mode 100644 web/www/routino/icons/limit-53.png create mode 100644 web/www/routino/icons/limit-54.png create mode 100644 web/www/routino/icons/limit-55.png create mode 100644 web/www/routino/icons/limit-56.png create mode 100644 web/www/routino/icons/limit-57.png create mode 100644 web/www/routino/icons/limit-58.png create mode 100644 web/www/routino/icons/limit-59.png create mode 100644 web/www/routino/icons/limit-6.0.png create mode 100644 web/www/routino/icons/limit-6.1.png create mode 100644 web/www/routino/icons/limit-6.2.png create mode 100644 web/www/routino/icons/limit-6.3.png create mode 100644 web/www/routino/icons/limit-6.4.png create mode 100644 web/www/routino/icons/limit-6.5.png create mode 100644 web/www/routino/icons/limit-6.6.png create mode 100644 web/www/routino/icons/limit-6.7.png create mode 100644 web/www/routino/icons/limit-6.8.png create mode 100644 web/www/routino/icons/limit-6.9.png create mode 100644 web/www/routino/icons/limit-6.png create mode 100644 web/www/routino/icons/limit-60.png create mode 100644 web/www/routino/icons/limit-61.png create mode 100644 web/www/routino/icons/limit-62.png create mode 100644 web/www/routino/icons/limit-63.png create mode 100644 web/www/routino/icons/limit-64.png create mode 100644 web/www/routino/icons/limit-65.png create mode 100644 web/www/routino/icons/limit-66.png create mode 100644 web/www/routino/icons/limit-67.png create mode 100644 web/www/routino/icons/limit-68.png create mode 100644 web/www/routino/icons/limit-69.png create mode 100644 web/www/routino/icons/limit-7.0.png create mode 100644 web/www/routino/icons/limit-7.1.png create mode 100644 web/www/routino/icons/limit-7.2.png create mode 100644 web/www/routino/icons/limit-7.3.png create mode 100644 web/www/routino/icons/limit-7.4.png create mode 100644 web/www/routino/icons/limit-7.5.png create mode 100644 web/www/routino/icons/limit-7.6.png create mode 100644 web/www/routino/icons/limit-7.7.png create mode 100644 web/www/routino/icons/limit-7.8.png create mode 100644 web/www/routino/icons/limit-7.9.png create mode 100644 web/www/routino/icons/limit-7.png create mode 100644 web/www/routino/icons/limit-70.png create mode 100644 web/www/routino/icons/limit-71.png create mode 100644 web/www/routino/icons/limit-72.png create mode 100644 web/www/routino/icons/limit-73.png create mode 100644 web/www/routino/icons/limit-74.png create mode 100644 web/www/routino/icons/limit-75.png create mode 100644 web/www/routino/icons/limit-76.png create mode 100644 web/www/routino/icons/limit-77.png create mode 100644 web/www/routino/icons/limit-78.png create mode 100644 web/www/routino/icons/limit-79.png create mode 100644 web/www/routino/icons/limit-8.0.png create mode 100644 web/www/routino/icons/limit-8.1.png create mode 100644 web/www/routino/icons/limit-8.2.png create mode 100644 web/www/routino/icons/limit-8.3.png create mode 100644 web/www/routino/icons/limit-8.4.png create mode 100644 web/www/routino/icons/limit-8.5.png create mode 100644 web/www/routino/icons/limit-8.6.png create mode 100644 web/www/routino/icons/limit-8.7.png create mode 100644 web/www/routino/icons/limit-8.8.png create mode 100644 web/www/routino/icons/limit-8.9.png create mode 100644 web/www/routino/icons/limit-8.png create mode 100644 web/www/routino/icons/limit-80.png create mode 100644 web/www/routino/icons/limit-81.png create mode 100644 web/www/routino/icons/limit-82.png create mode 100644 web/www/routino/icons/limit-83.png create mode 100644 web/www/routino/icons/limit-84.png create mode 100644 web/www/routino/icons/limit-85.png create mode 100644 web/www/routino/icons/limit-86.png create mode 100644 web/www/routino/icons/limit-87.png create mode 100644 web/www/routino/icons/limit-88.png create mode 100644 web/www/routino/icons/limit-89.png create mode 100644 web/www/routino/icons/limit-9.0.png create mode 100644 web/www/routino/icons/limit-9.1.png create mode 100644 web/www/routino/icons/limit-9.2.png create mode 100644 web/www/routino/icons/limit-9.3.png create mode 100644 web/www/routino/icons/limit-9.4.png create mode 100644 web/www/routino/icons/limit-9.5.png create mode 100644 web/www/routino/icons/limit-9.6.png create mode 100644 web/www/routino/icons/limit-9.7.png create mode 100644 web/www/routino/icons/limit-9.8.png create mode 100644 web/www/routino/icons/limit-9.9.png create mode 100644 web/www/routino/icons/limit-9.png create mode 100644 web/www/routino/icons/limit-90.png create mode 100644 web/www/routino/icons/limit-91.png create mode 100644 web/www/routino/icons/limit-92.png create mode 100644 web/www/routino/icons/limit-93.png create mode 100644 web/www/routino/icons/limit-94.png create mode 100644 web/www/routino/icons/limit-95.png create mode 100644 web/www/routino/icons/limit-96.png create mode 100644 web/www/routino/icons/limit-97.png create mode 100644 web/www/routino/icons/limit-98.png create mode 100644 web/www/routino/icons/limit-99.png create mode 100644 web/www/routino/icons/limit-no.png create mode 100644 web/www/routino/icons/marker-0-grey.png create mode 100644 web/www/routino/icons/marker-0-red.png create mode 100644 web/www/routino/icons/marker-1-grey.png create mode 100644 web/www/routino/icons/marker-1-red.png create mode 100644 web/www/routino/icons/marker-2-grey.png create mode 100644 web/www/routino/icons/marker-2-red.png create mode 100644 web/www/routino/icons/marker-3-grey.png create mode 100644 web/www/routino/icons/marker-3-red.png create mode 100644 web/www/routino/icons/marker-4-grey.png create mode 100644 web/www/routino/icons/marker-4-red.png create mode 100644 web/www/routino/icons/marker-5-grey.png create mode 100644 web/www/routino/icons/marker-5-red.png create mode 100644 web/www/routino/icons/marker-6-grey.png create mode 100644 web/www/routino/icons/marker-6-red.png create mode 100644 web/www/routino/icons/marker-7-grey.png create mode 100644 web/www/routino/icons/marker-7-red.png create mode 100644 web/www/routino/icons/marker-8-grey.png create mode 100644 web/www/routino/icons/marker-8-red.png create mode 100644 web/www/routino/icons/marker-9-grey.png create mode 100644 web/www/routino/icons/marker-9-red.png create mode 100644 web/www/routino/icons/marker-home-grey.png create mode 100644 web/www/routino/icons/marker-home-red.png create mode 100644 web/www/routino/icons/waypoint-add.png create mode 100644 web/www/routino/icons/waypoint-centre.png create mode 100644 web/www/routino/icons/waypoint-down.png create mode 100644 web/www/routino/icons/waypoint-home.png create mode 100644 web/www/routino/icons/waypoint-remove.png create mode 100644 web/www/routino/icons/waypoint-up.png create mode 100644 web/www/routino/index.html create mode 100644 web/www/routino/maplayout-ie6-bugfixes.css create mode 100644 web/www/routino/maplayout-ie7-bugfixes.css create mode 100644 web/www/routino/maplayout.css create mode 100755 web/www/routino/noscript.cgi create mode 100644 web/www/routino/noscript.html create mode 100644 web/www/routino/noscript.template.html create mode 100644 web/www/routino/page-elements.css create mode 100644 web/www/routino/page-elements.js create mode 100644 web/www/routino/paths.pl create mode 100755 web/www/routino/results.cgi create mode 100755 web/www/routino/router.cgi create mode 100644 web/www/routino/router.css create mode 120000 web/www/routino/router.html create mode 100644 web/www/routino/router.html.en create mode 100644 web/www/routino/router.js create mode 100644 web/www/routino/router.pl create mode 100755 web/www/routino/statistics.cgi create mode 100755 web/www/routino/visualiser.cgi create mode 100644 web/www/routino/visualiser.css create mode 100644 web/www/routino/visualiser.html create mode 100644 web/www/routino/visualiser.js create mode 100644 xml/Makefile create mode 100644 xml/osm.xsd create mode 100644 xml/routino-osm.xsd create mode 100644 xml/routino-profiles.xml create mode 100644 xml/routino-profiles.xsd create mode 100644 xml/routino-tagging-nomodify.xml create mode 100644 xml/routino-tagging.xml create mode 100644 xml/routino-tagging.xsd create mode 100644 xml/routino-translations.xml create mode 100644 xml/routino-translations.xsd create mode 100644 xml/xsd.xsd diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..7c79159 --- /dev/null +++ b/ChangeLog @@ -0,0 +1,1732 @@ +2010-07-10 Andrew M. Bishop + + Version 1.4.1 released + +2010-07-10 Andrew M. Bishop + + * doc/NEWS.txt: Update NEWS for release. + + * doc/ALGORITHM.txt: + Update documentation for slight modification to algorithm, also add more + information about how preferences etc are handled. + +2010-07-09 Andrew M. Bishop + + * src/Makefile: + Default compilation flags include optimisation and not debugging symbols. + +2010-07-08 Andrew M. Bishop + + * src/nodes.c: + Fix error with finding closest segment to the specified point. + + * src/optimiser.c: + Bug fix for not crashing when finding the middle part of the route. + +2010-07-07 Andrew M. Bishop + + * src/results.c, src/optimiser.c: + Changed the amount of memory allocated for intermediate results => routes much + faster. + + * src/output.c: Remove compilation warning. + + * src/Makefile: + Copy files to web directory like done in other Makefiles. + + * doc/Makefile: + Change location of HTML files in web directory and clean up web directory on distclean. + + * src/xml/Makefile: Stop message being printed when make runs. + + * xml/Makefile: + Fix error from last checkin and clean up web directory on distclean. + +2010-07-06 Andrew M. Bishop + + * src/optimiser.c: + Don't crash if the middle part of the route can't be found but exit cleanly. + +2010-07-05 Andrew M. Bishop + + * src/superx.c: Change the algorithm used to determine supernodes. + +2010-07-03 Andrew M. Bishop + + * xml/routino-translations.xml: + Added German translation [patch from Christoph Eckert]. + + * src/translations.c: + Don't crash if more than one language is in translations.xml but --language + option is not used. + +2010-06-28 Andrew M. Bishop + + * src/router.c: Don't crash if start and finish are the same point. + +2010-06-27 Andrew M. Bishop + + * doc/DATA.txt: New file. + + * doc/ALGORITHM.txt, doc/CONFIGURATION.txt, doc/INSTALL.txt, doc/OUTPUT.txt, doc/README.txt, + doc/TAGGING.txt, doc/USAGE.txt: + Updated documentation to match new web site. + + * doc/Makefile: New file. + + * xml/Makefile: Add some new variables. + +2010-06-26 Andrew M. Bishop + + * xml/routino-profiles.xml, xml/routino-tagging-nomodify.xml, xml/routino-tagging.xml, + xml/routino-translations.xml, src/translations.c: + Changed URLs to http://www.routino.org/ + + * doc/README.txt: *** empty log message *** + + * doc/OUTPUT.txt: Changed URLs to http://www.routino.org/ + +2010-05-31 Andrew M. Bishop + + Version 1.4 released + +2010-05-31 Andrew M. Bishop + + * doc/INSTALL.txt, doc/NEWS.txt, doc/README.txt: + Update for version 1.4. + + * src/xml/Makefile: Make sure that distclean really cleans up. + + * Makefile: Make sure that xml sub-directory is made. + + * src/router.c: + Fix the code that should stop routing if no segment is found. + +2010-05-30 Andrew M. Bishop + + * doc/USAGE.txt: + Add the planetsplitter tagging rules option (and remove the unnecessary options + that it replaces), add the filedumper OSM dump option and add the tagmodifier + program. + + * doc/TAGGING.txt: Describe the new tagging rules. + + * doc/OUTPUT.txt: Note that the HTML and GPX outputs are translated. + + * doc/CONFIGURATION.txt: Add the tagging rules configuration file. + + * doc/ALGORITHM.txt: An update to the current size of the UK database. + + * xml/routino-tagging-nomodify.xml: New file. + + * src/tagmodifier.c: + A tagging XML file must be read (just like planetsplitter). + + * src/filedumper.c: + Add the option to dump a region rather than all and to not output super + segments. + + * src/optimiser.c: Fix printing the number of super-segments tried. + +2010-05-29 Andrew M. Bishop + + * xml/routino-translations.xml, xml/routino-translations.xsd, src/ways.h, src/filedumper.c, + src/osmparser.c, src/output.c, src/translations.c, src/translations.h: + Translate the names given to unnamed roads (the highway type). + + * src/profiles.c, src/profiles.h, src/router.c: + Stricter check on specified profile before routing. + + * src/router.c: Ensure that if no segment is found the routing stops. + + * src/nodes.c: + When finding a closest segment one of the nodes must be within the search + distance. + +2010-05-28 Andrew M. Bishop + + * src/router.c: Make sure that some profiles are loaded. + +2010-05-27 Andrew M. Bishop + + * src/optimiser.c, src/profiles.c: + Fix bug with profile preferences (used incorrectly in route optimisation). + + * src/Makefile, src/filedumper.c, src/types.c, src/types.h: + Add an option to filedumper to dump an OSM format file. + +2010-05-25 Andrew M. Bishop + + * src/xmlparse.l: Fix bug with encoding XML strings. + +2010-05-23 Andrew M. Bishop + + * xml/Makefile: + Make sure that modified files are copied to web directory. + + * src/tagmodifier.c: + Fix bug when filename is specified on command line. + + * src/tagging.c, src/tagging.h, src/tagmodifier.c, src/xmlparse.l, src/osmparser.c: + Fix some memory leaks. + + * src/tagmodifier.c, xml/osm.xsd, xml/routino-osm.xsd, src/osmparser.c: + Add the 'bound' element to the XML parser. + +2010-05-22 Andrew M. Bishop + + * src/functionsx.h, src/osmparser.c, src/planetsplitter.c, src/ways.h, src/waysx.c, + src/waysx.h: + Remove the --transport=, --not-highway= and + --not-property= options from planetsplitter because they can be done + by the tagging.xml file now. + +2010-05-18 Andrew M. Bishop + + * src/Makefile: Add tagmodifier program. + + * src/xmlparse.l: Handle floating point numbers in scientific notation. + + * src/planetsplitter.c: + Read in the tag transformation rules before calling the OSM parser. + + * src/functionsx.h, src/osmparser.c: + Almost completely re-written OSM parser using tagging transformations. + + * src/tagmodifier.c, src/tagging.h, src/tagging.c: New file. + + * xml/Makefile: Copy the tagging rules to the web directory. + + * xml/routino-tagging.xml, xml/routino-tagging.xsd, xml/routino-osm.xsd: + New file. + + * xml/osm.xsd: Small fix for OSM schema. + +2010-05-14 Andrew M. Bishop + + * src/types.c: Remove highway type aliases from HighwayType() function. + + * src/xmlparse.h, src/xmlparse.l: Allow empty strings to be returned. + +2010-05-10 Andrew M. Bishop + + * src/xmlparse.h, src/xmlparse.l: + The line number is now a long integer. + + * src/xml/Makefile: Running 'make test' now compiles everything first. + +2010-04-28 Andrew M. Bishop + + * src/xml/Makefile: Delete zero length file if xsd-to-xmlparser fails. + + * src/nodes.c, src/nodesx.c, src/segments.c, src/segmentsx.c, src/ways.c, src/waysx.c: + Change file format to allow 64-bit off_t type with 32 bit void* type. + + * src/Makefile, src/filedumper.c, src/xml/Makefile: + Compile with _FILE_OFFSET_BITS=64 to get 64-bit fopen() and stat(). + +2010-04-27 Andrew M. Bishop + + * src/output.c: Fix mistake of writing GPX information to wrong file. + + * doc/OUTPUT.txt, doc/CONFIGURATION.txt: New file. + + * doc/TAGGING.txt, doc/USAGE.txt, doc/ALGORITHM.txt, doc/INSTALL.txt, doc/NEWS.txt, + doc/README.txt: + Interim checkin of updated documentation. + +2010-04-24 Andrew M. Bishop + + * src/router.c: + Merged the three functions to output the head/body/tail of the results back into + a single function. Added the '--output-none' option. + + * src/functions.h, src/output.c: + Merged the three functions to output the head/body/tail of the results back into + a single function. + + * xml/routino-translations.xml, xml/routino-translations.xsd, src/output.c, + src/translations.c, src/translations.h: + Added translations for the HTML output. + + * src/xmlparse.h, src/xmlparse.l: Changed functions from const. + + * src/output.c: + Add the copyright information into the translations.xml file instead of the + separate copyright.txt file. Add the translated copyright strings into the + outputs. + + * src/functions.h, src/router.c, src/translations.c, src/translations.h: + Add the copyright information into the translations.xml file instead of the + separate copyright.txt file. + + * src/xmlparse.h, src/xmlparse.l: + Add an option to not convert the XML strings into decoded representations (saves + converting them back later for the translated strings). + +2010-04-23 Andrew M. Bishop + + * src/xml/xsd-to-xmlparser.c, src/translations.c, src/xmlparse.h, src/xmlparse.l, + src/profiles.c: + Pass the tag name to the tag function. + +2010-04-22 Andrew M. Bishop + + * Makefile: Fix bug in makefile. + + * xml/Makefile: Move the translations into the web directory. + + * xml/routino-translations.xml, xml/routino-translations.xsd: New file. + + * src/output.c: Changed HTML output to be useful in web pages. + + * src/xmlparse.l: + Restart properly so that a different file can be read. + +2010-04-13 Andrew M. Bishop + + * src/xml/xsd-to-xmlparser.c, src/profiles.c, src/translations.c: + Name the tag variables and functions after the XSD data type and not the tag + name that uses it. + +2010-04-12 Andrew M. Bishop + + * src/profiles.c, src/translations.c, src/xmlparse.h, src/xmlparse.l, + src/xml/xsd-to-xmlparser.c, src/xml/Makefile: + Change the last parameter to the ParseXML function to be general options. + + * src/Makefile, src/types.h, src/ways.c, src/ways.h: + Move the type checking/printing functions from way.c to type.c. + + * src/types.c: New file. + +2010-04-11 Andrew M. Bishop + + * src/xml/xsd-to-xmlparser.c, src/profiles.c, src/translations.c, src/xmlparse.h, + src/xmlparse.l: + Added helper functions for parsing strings into numbers. + Added macros to perform common error checking. + Change XML parser callback functions to return an error status. + +2010-04-10 Andrew M. Bishop + + * src/router.c: Fix usage information. + + * src/translations.h, src/translations.c: New file. + + * src/output.c: Added translations for GPX and turn/heading. + + * src/Makefile, src/router.c: + Added file of translations and language selection. + +2010-04-09 Andrew M. Bishop + + * src/functions.h, src/planetsplitter.c, src/sorting.c: + Add an option '--sort-ram-size' to specify the RAM to use for sorting - defaults + to 256MB if not using slim mode. + +2010-04-08 Andrew M. Bishop + + * src/xml/Makefile: Fix test program generation and running. + + * src/xmlparse.h, src/xmlparse.l: + Make the strings const and add the number of attributes to the xmltag structure. + Add functions to convert character entities and character references. + + * src/profiles.c, src/xml/xsd-to-xmlparser.c: + Make the strings const and add the number of attributes to the xmltag structure. + +2010-04-07 Andrew M. Bishop + + * xml/Makefile: New file. + +2010-04-06 Andrew M. Bishop + + * src/Makefile: + Remove special lex/flex flags. Remove profiles.o from planetsplitter. + + * src/xml/xsd-to-xmlparser.c: + Don't print anything for attributes that are not set. + + * src/xmlparse.l: + Change error message for bad character in a quoted string. + Make sure attribute values are cleared before calling tag function (for + end-tags). + +2010-04-04 Andrew M. Bishop + + * src/xml/Makefile: Add some XML parsing test cases. + + * src/xml/xsd-to-xmlparser.c: Rename the XML handling function. + + * src/xmlparse.h, src/xmlparse.l, src/profiles.c: Added error checking. + +2010-04-03 Andrew M. Bishop + + * src/functionsx.h, src/osmparser.c, src/planetsplitter.c: + Rename the old ParseXML() function as ParseOSM(). + +2010-04-01 Andrew M. Bishop + + * src/output.c: Wrap GPX descriptions in CDATA. + +2010-03-31 Andrew M. Bishop + + * xml/routino-profiles.xml: New file. + + * src/xml/xsd-to-xmlparser.c, src/profiles.c, src/xmlparse.h, src/xmlparse.l: + Call the XML tag functions for the end tags as well as the start tags. + +2010-03-30 Andrew M. Bishop + + * src/profiles.c, src/profiles.h: + Change the name of the --profile-json and --profile-perl options. + + * src/filedumper.c, src/planetsplitter.c, src/router.c: + Improve the program help messages. + +2010-03-29 Andrew M. Bishop + + * src/files.c, src/functions.h, src/profiles.c, src/profiles.h, src/router.c: + Added command line option to specify a file containing profiles. + Added command line option to select profile by name from loaded set. + Use XML parser to read in the profiles. + + * src/Makefile: Better handling of the xml sub-directory. + + * src/xml/xsd-to-xmlparser.c: + Add the option to ignore unknown attributes. + Print out the skeleton file using static functions and variables. + + * src/xml/Makefile: Keep the intermediate files. + + * src/xmlparse.h, src/xmlparse.l: + Add the option to ignore unknown attributes. + +2010-03-28 Andrew M. Bishop + + * src/profiles.h, src/router.c, src/profiles.c: + Add an option to print out the profiles as XML format. + + * src/xmlparse.h, xml/xsd.xsd, xml/osm.xsd, src/xml/xsd-to-xmlparser.c: + New file. + + * src/Makefile: Added the XML subdirectory and xmlparser.c. + + * src/xmlparse.l, src/xml/Makefile: New file. + +2010-03-20 Andrew M. Bishop + + * src/output.c: Add descriptions to each point in the GPX route file. + + * src/files.c, src/functions.h, src/nodesx.c, src/output.c, src/segmentsx.c, src/waysx.c: + Move the stat() calls to find a file size into a helper function in files.c. + + * src/files.c, src/output.c, src/planetsplitter.c: + Improve the error messages by adding strerror() to them. + + * src/filedumper.c, src/router.c: + Don't check the return value of the functions to load the nodes, segments and + ways because those functions will exit in case of an error. + + * src/nodes.c, src/segments.c, src/ways.c: + Don't check the return value of MapFile() because it will exit in case of an + error. + + * src/planetsplitter.c: + Allow filenames on the planetsplitter command line. + +2010-03-19 Andrew M. Bishop + + * src/waysx.h, src/filedumper.c, src/files.c, src/functions.h, src/nodesx.c, src/nodesx.h, + src/planetsplitter.c, src/segmentsx.c, src/segmentsx.h, src/superx.c, src/waysx.c: + Allow planetsplitter to be run with a --parse-only or --process-only option and + append to existing file or read from existing file. + +2010-03-18 Andrew M. Bishop + + * src/router.c: Fix usage message error and shuffle order. + + * src/output.c, src/router.c: + Allow selection of which outputs are to be created. + +2010-03-17 Andrew M. Bishop + + * src/output.c: Re-order the code for HTML. + +2010-03-15 Andrew M. Bishop + + * src/output.c: Create a simple HTML output. + +2010-03-06 Andrew M. Bishop + + * src/router.c, src/nodes.c: + Speed up start/via/stop point search algorithm. + +2010-03-05 Andrew M. Bishop + + * src/profiles.c: + Change the format of the output for the --help-profile-{pl|js} options. + +2010-01-21 Andrew M. Bishop + + Version 1.3 released + +2010-01-21 Andrew M. Bishop + + * doc/NEWS.txt: Update to latest news. + +2010-01-18 Andrew M. Bishop + + * doc/USAGE.txt, doc/TAGGING.txt, doc/INSTALL.txt: + Updated documentation. + +2010-01-15 Andrew M. Bishop + + * src/router.c, src/functions.h: + Change the test output formats to add turn, node type and bearing information. + +2010-01-13 Andrew M. Bishop + + * src/output.c: + Change the test output formats to add turn, node type and bearing information. + +2009-12-16 Andrew M. Bishop + + * src/router.c: + Added an option to use only nodes and not interpolate a point into a segment. + +2009-12-15 Andrew M. Bishop + + * src/osmparser.c, src/profiles.c, src/types.h, src/ways.c: + Added wheelchair as type of transport. + +2009-12-13 Andrew M. Bishop + + * src/osmparser.c, src/profiles.c, src/types.h, src/ways.c: + Add bridge and tunnel to highway properties. + +2009-12-12 Andrew M. Bishop + + * src/Makefile: + Ignore the error if executables cannot be copied after compiling. + + * src/functions.h, src/nodesx.c, src/segmentsx.c, src/sorting.c, src/waysx.c: + Add some FILESORT_* #defines and use them. + +2009-12-11 Andrew M. Bishop + + * src/functions.h, src/nodesx.c, src/planetsplitter.c, src/segmentsx.c, src/sorting.c, + src/waysx.c, src/waysx.h: + Added a new function to sort variable length data - simplifies the compacting of + ways, reduces memory usage potentially required for it and simplifies the code. + +2009-12-10 Andrew M. Bishop + + * src/waysx.c: + Write out the list of ways without memory mapping anything. + +2009-11-27 Andrew M. Bishop + + * src/osmparser.c, src/profiles.c, src/types.h, src/ways.c: + Add in "multilane" as a new highway property. + +2009-11-25 Andrew M. Bishop + + * src/filedumper.c, src/optimiser.c, src/osmparser.c, src/planetsplitter.c, src/profiles.h, + src/router.c, src/ways.h, src/waysx.c, src/waysx.h: + Store the selected options when parsing (planetsplitter) and display them in the + statistics (filedumper) and check them when routing (router). + +2009-11-23 Andrew M. Bishop + + * src/osmparser.c, src/output.c, src/profiles.c, src/types.h, src/ways.c: + Add in "steps" as a new highway type. + +2009-11-19 Andrew M. Bishop + + * src/optimiser.c, src/router.c: + Made the verbose output consistent between different places. + +2009-11-18 Andrew M. Bishop + + * src/router.c: Fix bug with previous segment-splitting routing. + +2009-11-14 Andrew M. Bishop + + * src/optimiser.c, src/output.c, src/router.c, src/segments.h, src/functions.h, src/nodes.c, + src/nodes.h: + If a selected waypoint is not very close to an existing node then insert a fake + node in the segment that comes closest and use that instead. + +2009-11-13 Andrew M. Bishop + + * src/optimiser.c, src/osmparser.c, src/queue.c, src/results.c, src/results.h, src/types.h: + Added in some more constants with the value ~0. + +2009-11-06 Andrew M. Bishop + + * src/filedumper.c: + Check the values for the --node=, --segment= and --way= options. + +2009-11-03 Andrew M. Bishop + + * src/output.c, src/planetsplitter.c, src/profiles.c, src/profiles.h, src/router.c, + src/types.h, src/ways.c: + Rename Way_Unknown to Way_Count to make more sense and match the properties. + +2009-11-02 Andrew M. Bishop + + * src/osmparser.c: Allow the tag "paved" as well as "surface=paved". + + * src/filedumper.c, src/optimiser.c, src/osmparser.c, src/planetsplitter.c, src/profiles.c, + src/profiles.h, src/router.c, src/types.h, src/ways.c, src/ways.h: + Added the ability to set routing preferences using highway properties. + Initially the only choice is either paved or unpaved but the code has been + updated to allow any number of properties to be added. + +2009-10-27 Andrew M. Bishop + + * src/osmparser.c: + Handle the "designation=..." tag for bridleway, byway and footpath. (Also + change to using a macro for testing if access is allowed and now allow + "destination"). + + * src/osmparser.c, src/profiles.c, src/types.h, src/ways.c, src/ways.h: + Added Moped to the list of transports (and incidentally increased the transport + data type to 16 bits and re-ordered the Way data-type in response). + +2009-10-26 Andrew M. Bishop + + * src/profiles.c: + Ensure that horses and bicycles have a default speed on trunk even though they + have a default preference not to use it. + + * src/osmparser.c, src/profiles.c, src/types.h, src/ways.c: + Re-ordered the types so that Horse comes before Bicycle. + + * src/osmparser.c, src/output.c, src/profiles.c, src/types.h, src/ways.c: + Remove the Bridleway and Footway highway types and use the Path type instead + (also re-ordered the types so that Cycleway comes before Path). + + * src/profiles.c: Remove unneeded spaces at the end of the output. + +2009-10-25 Andrew M. Bishop + + * src/output.c: + Fix bug in code that determines waypoints for abbreviated output. + +2009-10-24 Andrew M. Bishop + + * src/functions.h, src/optimiser.c, src/router.c: + Fix missing segments in output if start and finish points are found by the start + search. + +2009-10-22 Andrew M. Bishop + + * src/files.c, src/nodesx.c, src/segmentsx.c, src/sorting.c, src/superx.c, src/waysx.c: + Added some missing comments and corrected some existing ones. + +2009-10-21 Andrew M. Bishop + + Version 1.2 released + +2009-10-21 Andrew M. Bishop + + * doc/README.txt, doc/USAGE.txt, doc/NEWS.txt: Updated for version 1.2. + +2009-10-20 Andrew M. Bishop + + * src/Makefile: Add sorting.o to the Makefile. + +2009-10-12 Andrew M. Bishop + + * src/waysx.c: When sorting we cannot have NULL pointers now. + + * src/nodesx.c, src/segmentsx.c, src/waysx.c: + Re-order the functions in the file into a more logical order. + No functional changes. + + * src/nodesx.c, src/planetsplitter.c, src/segmentsx.c, src/sorting.c, src/waysx.c: + Rename the tmpdirname variable. + +2009-10-10 Andrew M. Bishop + + * src/nodesx.c, src/osmparser.c, src/segmentsx.c, src/sorting.c, src/waysx.c: + Corrections after running with valgrind. + + * src/planetsplitter.c: Fix early termination test. + + * src/nodesx.c, src/nodesx.h, src/segmentsx.c: + Remove the nodesx->gdata index. + +2009-10-09 Andrew M. Bishop + + * src/nodesx.c, src/segmentsx.c, src/typesx.h, src/waysx.c, src/waysx.h: + Free the nodesx->super array and the segmentsx->firstnode array when finished + with them. Remove wayx->cid and overwrite wayx->id instead. Overwrite + nodex[i]->id=i for later geographically sorted use. + +2009-10-08 Andrew M. Bishop + + * src/nodesx.c, src/planetsplitter.c, src/segmentsx.c, src/segmentsx.h, src/superx.c: + Replace node, segment and way indexes with a single index for a set of segments + containing the location of the first segment for each node. + + * src/nodesx.h: Fix comment. + +2009-10-07 Andrew M. Bishop + + * src/osmparser.c, src/segmentsx.c, src/superx.c: + AppendSegment adds a single segment and not a pair. + + * src/waysx.c: Use heapsort() instead of qsort(). + + * src/nodesx.c, src/nodesx.h, src/planetsplitter.c, src/segmentsx.c, src/segmentsx.h, + src/superx.c, src/waysx.c: + Go back to the version 1.1 method of having each segment listed twice. This + simplifies the lookup of first/next segments at no in-RAM index cost and now + that slim mode has sorting of file contents the balance has tipped back. + +2009-10-04 Andrew M. Bishop + + * src/functions.h, src/sorting.c: + Change the sort function to allow the indexing callback to veto the write. + + * src/nodesx.c: Remove the duplicates when sorting. + + * src/waysx.c: + Sort the ways using the same method as the nodes. Also remove the duplicates. + + * src/nodesx.c: + Use the new sort functions to allow sorting the data in the file without needing + to read (or mmap) the whole file into RAM at the same time. + + * src/functions.h: Add some functions to perform sorting. + + * src/sorting.c: New file. + + * src/queue.c: Fix bug with binary heap sort. + +2009-09-25 Andrew M. Bishop + + * src/queue.c: Add comments describing the algorithm used. + +2009-09-23 Andrew M. Bishop + + * src/nodesx.c, src/waysx.c: + Simplify the de-duplication when sorting and update some comments. + +2009-09-22 Andrew M. Bishop + + * src/nodesx.c, src/nodesx.h: + Remove a leftover from the last change on these files. + + * src/segmentsx.c: Improve the super-segment de-duplication. + +2009-09-21 Andrew M. Bishop + + * src/nodesx.c, src/nodesx.h, src/planetsplitter.c: + Remove the non-highway nodes without re-sorting the whole list again. + +2009-09-17 Andrew M. Bishop + + * src/osmparser.c, src/planetsplitter.c, src/segmentsx.c, src/superx.c, src/waysx.c, + src/waysx.h: + Added the slim mode to Ways as well. + + * src/ways.h: Add padding to Ways structure to allow it to be zeroed. + + * src/nodesx.c: Add some comments when closing and re-opening files. + + * src/files.c, src/functions.h: + The WriteFile function now has a const parameter. + +2009-09-15 Andrew M. Bishop + + * src/nodesx.c, src/nodesx.h, src/planetsplitter.c, src/segmentsx.c: + Some bug fixes and some missing unmap function calls. + +2009-09-07 Andrew M. Bishop + + * src/segmentsx.h, src/superx.c, src/nodesx.c, src/nodesx.h, src/segmentsx.c: + Fixed slim mode for segments and nodes (slim now means mapping only one file + into RAM at a time and none when creating the final output). + +2009-09-06 Andrew M. Bishop + + * src/nodesx.h, src/planetsplitter.c, src/segmentsx.c, src/segmentsx.h, src/superx.c, + src/superx.h, src/nodesx.c: + Slim version of segments code (still very slow and only works on simple cases). + + * src/files.c, src/functions.h: + Remove the delete option from UnmapFile() and make it return NULL. + + * src/filedumper.c: Allow dumping all nodes, segments or ways. + +2009-09-05 Andrew M. Bishop + + * src/nodesx.c: Don't re-sort unnecessarily. + + * src/nodesx.c, src/nodesx.h, src/planetsplitter.c, src/segmentsx.c, src/superx.c: + Improve slim mode for nodes so that no data is not loaded into RAM at all. + + * src/files.c, src/functions.h: Add some more file functions. + +2009-09-03 Andrew M. Bishop + + * src/nodesx.c, src/files.c, src/functions.h: + Remove extra argument from MapFile function. + + * src/nodesx.c, src/nodesx.h, src/planetsplitter.c, src/segmentsx.c, src/superx.c: + Added slim mode (--slim) to planetsplitter for nodes only. + + * src/files.c, src/functions.h: + Changes to mapping and unmapping files for slim mode. + +2009-08-25 Andrew M. Bishop + + * src/planetsplitter.c: Revert the order that the functions are called. + + * src/nodesx.c: Fix for assert statement. + + * src/files.c: Bug fix for mmap(). + +2009-08-20 Andrew M. Bishop + + * src/osmparser.c: Fix bug with memory allocation. + +2009-08-19 Andrew M. Bishop + + * src/nodesx.c, src/nodesx.h, src/planetsplitter.c, src/segmentsx.c, src/segmentsx.h, + src/superx.c, src/waysx.c, src/waysx.h: + Remove "sorted" parameter in data structure and change assert statements. + +2009-08-17 Andrew M. Bishop + + * src/router.c: + Increase to 99 the number of waypoints that can be specified. + +2009-08-15 Andrew M. Bishop + + * src/queue.c: Fix comment. + + * src/Makefile: + Tidy the compilation options to make it easier to turn them on and off. + + * src/router.c: + Remove the --all, --super and --no-output command line options. + Handle the renamed routing functions. + + * src/functions.h, src/optimiser.c: + Rename the routing functions and make FindRoute only find routes with no + super-nodes in them. + + * src/queue.c: + When popping from queue make sure that place in queue is cleared. + + * src/optimiser.c, src/queue.c, src/results.c, src/results.h, src/superx.c: + Optimise the priority queue used for routing. + + * src/filedumper.c: Fix dumping nodes when they are super-nodes. + +2009-07-23 Andrew M. Bishop + + * src/Makefile, src/optimiser.c, src/results.c, src/results.h, src/superx.c: + Split off queue functions into a separate file. + + * src/queue.c: New file. + +2009-07-19 Andrew M. Bishop + + * src/nodesx.c, src/segments.h, src/segmentsx.c, src/ways.h, src/waysx.c, src/filedumper.c, + src/nodes.h: + Include the number of super-nodes, super-segments etc in the database as useful + information to put in the statistics output. + + * src/superx.c: Fix incorrect progress indicator message. + + * src/waysx.c: Fix problem with memory reallocation. + + * src/nodesx.c, src/osmparser.c, src/planetsplitter.c, src/segmentsx.c, src/segmentsx.h, + src/superx.c: + Store only one copy of each segment but index once for each direction. + +2009-07-12 Andrew M. Bishop + + * src/functionsx.h, src/nodesx.c, src/nodesx.h, src/osmparser.c, src/output.c, + src/planetsplitter.c, src/profiles.c, src/results.c, src/segments.c, src/segmentsx.c, + src/segmentsx.h, src/superx.c, src/superx.h, src/ways.h, src/waysx.c, src/waysx.h: + Tidy up and fix comments and include files. + + * src/osmparser.c, src/planetsplitter.c, src/router.c, src/segmentsx.c, src/superx.c, + src/waysx.c, src/filedumper.c, src/nodesx.c, src/optimiser.c: + Check all print statements and made them more consistent and/or accurate. + +2009-07-11 Andrew M. Bishop + + * src/nodesx.c, src/nodesx.h, src/planetsplitter.c, src/segmentsx.c, src/waysx.c, + src/waysx.h: + Free memory at the end of planetsplitter (to aid finding potential leaks + earlier). + +2009-07-09 Andrew M. Bishop + + * src/segmentsx.c: Free memory correctly (really). + + * src/planetsplitter.c, src/waysx.c, src/waysx.h: + Separate the sorting of Ways from compacting of Ways. + + * src/nodes.h, src/nodesx.c, src/nodesx.h, src/segmentsx.c, src/visualiser.c, + src/filedumper.c, src/nodes.c: + Rename structure members after recent changes. + + * src/segmentsx.c: Free memory correctly. + + * src/types.h, src/segmentsx.c: Fix duplicate checking. + + * src/planetsplitter.c: Ensure that variable is reset before using it. + + * src/types.h, src/visualiser.c, src/visualiser.h, src/filedumper.c, src/nodes.c, + src/nodes.h, src/nodesx.c, src/nodesx.h, src/optimiser.c, src/osmparser.c, src/output.c, + src/router.c, src/segments.c, src/segments.h, src/segmentsx.c: + Change from float to double for latitude and longitude. + Store latitude and longitude as an integer type rather than float (higher precision). + +2009-07-08 Andrew M. Bishop + + * src/superx.c: Ensure that variable is reset before using it. + +2009-07-06 Andrew M. Bishop + + * src/visualiser.c: + Print all super-segments within and crossing the border. + Don't display speed limits for tracks and paths unless set. + +2009-07-04 Andrew M. Bishop + + * src/segmentsx.h, src/superx.c, src/waysx.c, src/waysx.h: + Change data structure to avoid calling realloc() each time to allocate more + memory. + +2009-07-02 Andrew M. Bishop + + * src/types.h, src/waysx.c, src/waysx.h: + Handle duplicate ways. + + * src/nodes.c, src/nodesx.c, src/planetsplitter.c, src/profiles.c, src/results.c, + src/segments.c, src/segmentsx.c, src/superx.c, src/superx.h, src/types.h, src/ways.c, + src/waysx.c: + Fix some gcc pedantic warnings. + + * src/files.c, src/nodesx.c, src/osmparser.c, src/results.c, src/router.c, src/segments.c, + src/segmentsx.c, src/superx.c, src/ways.c, src/waysx.c: + Removed unused header files, change assert statements, tidy some code. + +2009-07-01 Andrew M. Bishop + + * src/nodesx.c, src/nodesx.h, src/planetsplitter.c, src/segmentsx.c, src/segmentsx.h, src/superx.c: + Remove the Node structure from the NodeX structure to save memory. + + * src/filedumper.c: + Print latitude and longitude in degrees. + +2009-06-30 Andrew M. Bishop + + * src/segmentsx.h: + Re-order the data in the structure. + + * src/nodesx.c, src/nodesx.h, src/osmparser.c, src/planetsplitter.c, src/segmentsx.c, + src/segmentsx.h, src/superx.c, src/waysx.h: + Remove the Segment structure from the SegmentX structure to save memory. + +2009-06-29 Andrew M. Bishop + + * src/filedumper.c, src/nodes.h, src/nodesx.c, src/segments.c, src/segments.h, + src/segmentsx.c, src/superx.c, src/types.h: + Move the super-segment and normal-segment flags from the nodes to the distance. + Remove the NODE() macro and rename SUPER_FLAG to NODE_SUPER. + + * src/waysx.c: Replace memmove with structure copy. + + * src/nodesx.c, src/segmentsx.c, src/segmentsx.h, src/superx.c: + Rename SegmentsX sdata to ndata. + +2009-06-25 Andrew M. Bishop + + * src/waysx.c, src/waysx.h: Rename part of the structure. + + * src/nodesx.c, src/nodesx.h, src/planetsplitter.c, src/segmentsx.c, src/segmentsx.h, + src/superx.c, src/waysx.h: + Undo part of the previous change - only update the Segment way index at the end. + + * src/waysx.h, src/nodesx.c, src/osmparser.c, src/planetsplitter.c, src/segmentsx.c, + src/segmentsx.h, src/superx.c, src/superx.h, src/typesx.h, src/waysx.c: + Reduce the number of ways in the output by compacting them (sharing the same + information between identical ways). + +2009-06-24 Andrew M. Bishop + + * src/filedumper.c, src/nodes.h: + Allow dumping out of nodes, segments and ways. + +2009-06-15 Andrew M. Bishop + + * src/segmentsx.c, src/superx.c, src/visualiser.c, src/ways.c, src/ways.h: + Rename WaysSame() with WaysCompare() and reverse the sense of the output. + + * src/functionsx.h, src/typesx.h: New file. + + * src/functions.h, src/nodesx.h, src/osmparser.c, src/planetsplitter.c, src/segmentsx.h, + src/superx.h, src/types.h, src/waysx.h: + Put some of types.h into typesx.h (for extended data types). + Put some of functions.h into functionsx.h (for OSM parser). + Change included files to match. + + * src/filedumper.c, src/osmparser.c, src/output.c, src/router.c, src/types.h, src/visualiser.c: + Add a macro for converting degrees to radians and radians to degrees. + + * src/optimiser.c: + Fix weight, height, width, length restriction routing. + + * doc/TAGGING.txt, src/osmparser.c: + Recognise tags "vehicle" and "motor_vehicle". + +2009-06-13 Andrew M. Bishop + + Version 1.1 released + +2009-06-13 Andrew M. Bishop + + * src/nodesx.c, src/planetsplitter.c, src/segmentsx.c, src/segmentsx.h: + Handle nodes that are missing from the .osm file (ignore the segment). + + * src/nodesx.c: + Revert the last change (Print an error message and exit if a node cannot be found). + + * doc/NEWS.txt: New file. + + * src/Makefile: + Delete the executables from the web directory for 'distclean'. + +2009-06-12 Andrew M. Bishop + + * doc/USAGE.txt, doc/INSTALL.txt, doc/README.txt: + Update the documentation. + + * src/Makefile: Copy the executables into the web directory. + +2009-06-08 Andrew M. Bishop + + * src/filedumper.c: Change help text. + + * src/visualiser.c: + Change format of super-node/segment visualiser output. + +2009-06-07 Andrew M. Bishop + + * doc/TAGGING.txt: Updated with imperial to metric conversions. + + * src/Makefile: Added visualiser.c. + + * src/filedumper.c: Now used for data visualisation and statistics. + + * src/visualiser.h, src/visualiser.c: New file. + +2009-06-05 Andrew M. Bishop + + * src/osmparser.c: + Improve parsing of imperial units (mph, feet & inches). + +2009-06-03 Andrew M. Bishop + + * src/nodesx.c: + Print an error message and exit if a node cannot be found. + +2009-05-31 Andrew M. Bishop + + * src/ways.c, src/ways.h, src/waysx.c, src/waysx.h: + Move function from waysx.c to ways.c. + +2009-05-29 Andrew M. Bishop + + * doc/USAGE.txt: + Update usage information with new options and copyright.txt usage. + + * src/nodes.c, src/nodes.h, src/router.c: + Make sure that the chosen "nearest point" is a highway that the profile allows. + +2009-05-23 Andrew M. Bishop + + * src/profiles.c: + Change the default profile; horses are slower, bicycles may be allowed on + footways (and similar). + +2009-05-15 Andrew M. Bishop + + * src/files.c, src/output.c: + Error checking on opening files (to read/write data and to write output). + +2009-05-14 Andrew M. Bishop + + * src/output.c, src/results.c, src/router.c, src/segments.c, src/segmentsx.c, src/superx.c, + src/types.h, src/nodes.c, src/nodesx.c, src/optimiser.c: + Replace ~0 or 0 with NO_NODE value for "no node" condition. + +2009-05-13 Andrew M. Bishop + + * src/output.c: + Remove one more NODE macro and fix an output formatting error. + + * src/nodes.c, src/nodes.h, src/optimiser.c, src/output.c, src/router.c: + Remove some node macros, change some node function arguments. + + * src/optimiser.c, src/profiles.c, src/profiles.h: + Move some common code into the profile. + + * src/superx.c: Remove distance and duration from Result structure. + + * src/output.c: Better junction detection. + + * src/optimiser.c, src/results.c, src/results.h: + Remove distance and duration from Result structure. + +2009-05-09 Andrew M. Bishop + + * src/output.c: + Add better junction detection for deciding on route waypoints. + +2009-05-06 Andrew M. Bishop + + * src/optimiser.c, src/profiles.c, src/profiles.h, src/types.h: + Route using preferences for each highway. + + * src/router.c: Print out longitude then latitude. + +2009-04-30 Andrew M. Bishop + + * src/results.h, src/router.c, src/superx.c, src/types.h, src/optimiser.c, src/osmparser.c, + src/planetsplitter.c, src/profiles.c, src/profiles.h, src/results.c: + First attempt at preferences for highways - uses integer arithmetic and doesn't + work well. + +2009-04-27 Andrew M. Bishop + + * src/functions.h, src/optimiser.c, src/output.c, src/results.c, src/results.h, src/router.c: + Allow generating a route with intermediate waypoints. + +2009-04-24 Andrew M. Bishop + + * src/functions.h, src/output.c, src/router.c: + Split the output functions into separate head/body/tail. + Read in an optional copyright.txt file and include contents in output. + +2009-04-23 Andrew M. Bishop + + * src/profiles.c: Improve Javascript and perl print out. + + * src/filedumper.c, src/files.c, src/functions.h, src/planetsplitter.c, src/router.c: + Move the filename generation to a new function. + +2009-04-22 Andrew M. Bishop + + * src/Makefile, src/functions.h, src/optimiser.c: + Split the function to print the output into a new file. + + * src/output.c: New file. + +2009-04-15 Andrew M. Bishop + + * src/osmparser.c: + Fix for parsing nodes from XML (no effect on results). + +2009-04-12 Andrew M. Bishop + + * doc/USAGE.txt, src/optimiser.c: + Create a GPX route as well as a track. + + * src/ways.c: Changed the license to Affero GPLv3. + +2009-04-10 Andrew M. Bishop + + * src/optimiser.c: + Add a waypoint to the GPX file for the start and finish points. + + * doc/USAGE.txt: + Include more information about the output file formats. + +2009-04-08 Andrew M. Bishop + + Version 1.0 released + +2009-04-08 Andrew M. Bishop + + * Makefile: New file. + + * src/Makefile: Fix dependency file generation. + + * doc/USAGE.txt, doc/TAGGING.txt, doc/README.txt, doc/INSTALL.txt, doc/ALGORITHM.txt: + New file. + + * src/Makefile, src/filedumper.c, src/files.c, src/functions.h, src/nodes.c, src/nodes.h, + src/nodesx.c, src/nodesx.h, src/optimiser.c, src/osmparser.c, src/planetsplitter.c, + src/profiles.c, src/profiles.h, src/results.c, src/results.h, src/router.c, src/segments.c, + src/segments.h, src/segmentsx.c, src/segmentsx.h, src/superx.c, src/superx.h, src/types.h, + src/ways.h, src/waysx.c, src/waysx.h: + Changed the license to Affero GPLv3. + +2009-04-07 Andrew M. Bishop + + * src/planetsplitter.c: Remove the --help-profile command line option. + +2009-03-28 Andrew M. Bishop + + * src/optimiser.c: + Fix file headers (again) and fix segment distance/duration for abbreviated text + output. + +2009-03-24 Andrew M. Bishop + + * src/osmparser.c, src/profiles.c, src/types.h, src/ways.c: + Added highway=path; defaults to foot=yes but also is defaulted for bicycle and + horse transport. + +2009-03-23 Andrew M. Bishop + + * src/optimiser.c: Fixed the header in the output text files. + + * src/osmparser.c: + Add parsing for *=designated allowing passage along a highway. + + * src/profiles.h, src/router.c, src/profiles.c: + Add a function to output default profiles as perl data structures. + +2009-03-21 Andrew M. Bishop + + * src/nodesx.c: + Handle duplicated nodes (e.g. from concatenated input files). + + * src/optimiser.c: Add a header to the output text files. + +2009-03-07 Andrew M. Bishop + + * src/optimiser.c: + Renamed the *.txt output to *-all.txt and added a new shorted *.txt output. + + * src/router.c: Renamed the --no-print option to --no-output. + +2009-03-04 Andrew M. Bishop + + * src/nodes.c: Fix bug with finding nearest node. + +2009-03-03 Andrew M. Bishop + + * src/superx.c: Fix the merging of super-segments. + +2009-03-01 Andrew M. Bishop + + * src/profiles.c, src/profiles.h: + Added more limits (weight, height, width, length). + + * src/segments.c: Use the lower speed from the profile and the way. + + * src/osmparser.c: Added more limits (weight, height, width, length). + Added highway=living_street and highway=services. + + * src/ways.c, src/ways.h, src/optimiser.c, src/router.c, src/segmentsx.c, src/superx.c, + src/types.h: + Added more limits (weight, height, width, length). + + * src/waysx.c, src/waysx.h: + Added a function to test if two ways are the same. + +2009-02-28 Andrew M. Bishop + + * src/nodesx.c: + Round the node location to avoid if falling into the wrong bin. + + * src/nodesx.c, src/planetsplitter.c, src/segmentsx.c, src/waysx.c: + Move print statements from planetsplitter into individual functions. + + * src/Makefile: Compile with optimisation and no profiling. + + * src/profiles.c, src/router.c: + Add new command line options to make it more CGI friendly. + +2009-02-27 Andrew M. Bishop + + * src/profiles.c, src/profiles.h, src/router.c: + Print out Javascript code containing the profiles. + +2009-02-24 Andrew M. Bishop + + * src/segmentsx.h, src/superx.c, src/nodesx.c, src/segments.c, src/segments.h, + src/segmentsx.c: + Remove segment->next1 since it always points at the next segment or nowhere. + + * src/profiles.c: Remove track from valid types for most transports. + +2009-02-15 Andrew M. Bishop + + * src/functions.h, src/optimiser.c, src/router.c: + Change some function names. + + * src/osmparser.c: Add in tests for motorcar=1 etc. + + * src/nodes.c, src/nodes.h, src/router.c: + The search to find a node given the lat/long now searches harder. + + * src/optimiser.c: Better test for failing to find a route. + + * src/router.c: Change --only-super to --super. + + * src/nodesx.c, src/optimiser.c, src/osmparser.c, src/router.c, src/segments.c, + src/segmentsx.c, src/types.h, src/nodes.c: + Store radians rather than degrees. + + * src/segments.c, src/segmentsx.c: + Change to sinf(), cosf(), sqrtf(), asinf() functions. + + * src/optimiser.c: + Set the sortby parameter to the minimum distance/duration consistent with the + travelled distance/duration and the remaining straight line distance with the + fastest possible speed. + + * src/filedumper.c, src/nodes.c, src/nodes.h, src/nodesx.c, src/types.h: + Add macros for handling lat/long to bin conversions. + + * src/osmparser.c: Handle oneway=1 and oneway=-1. + +2009-02-10 Andrew M. Bishop + + * src/results.c, src/results.h: + Added a new 'sortby' entry to the Result. + Changed node_t to index_t. + + * src/router.c: Changed node_t to index_t. + + * src/nodes.c, src/segments.c, src/segments.h: + Change the Distance() function to return distance_t. + +2009-02-08 Andrew M. Bishop + + * src/optimiser.c, src/results.c, src/results.h, src/router.c, src/superx.c: + Calculate quickest or shortest, not both. + + * src/optimiser.c, src/profiles.c, src/router.c: + Give appropriate error messages if start or end of route are not possible. + +2009-02-07 Andrew M. Bishop + + * src/results.c: + Slight speedup by doing a linear search when looking up results and not storing + in sorted order. + + * src/superx.h, src/superx.c, src/waysx.h, src/waysx.c, src/segmentsx.h, src/segmentsx.c, + src/nodesx.h, src/nodesx.c: + New file. + + * src/ways.h, src/Makefile, src/filedumper.c, src/functions.h, src/nodes.c, src/nodes.h, + src/optimiser.c, src/osmparser.c, src/planetsplitter.c, src/router.c, src/segments.c, + src/segments.h, src/types.h, src/ways.c: + Split the extended data types from the normal data types. + + * src/nodes.c: Return NULL if the node cannot be found. + + * src/Makefile, src/filedumper.c, src/optimiser.c, src/router.c: + Add new command line options. + + * src/supersegments.c: Fix some status messages. + + * src/optimiser.c, src/types.h: Routing works with super-nodes now. + +2009-02-06 Andrew M. Bishop + + * src/ways.c, src/segments.c, src/segments.h, src/supersegments.c, src/types.h, src/nodes.c, + src/nodes.h, src/optimiser.c, src/osmparser.c, src/planetsplitter.c, src/functions.h: + Segments now not duplicated in database. + Routing with all nodes works, not with super-nodes. + +2009-02-04 Andrew M. Bishop + + * src/router.c: Fix usage output. + + * src/ways.c, src/ways.h: Only sort once, don't store the index. + + * src/planetsplitter.c, src/router.c: + Use '--*' command line arguments, not '-*'. + + * src/nodes.c, src/router.c, src/segments.c, src/ways.c: + Make sure that nodes, segments and ways could be loaded. + + * src/nodes.h, src/optimiser.c, src/router.c, src/segments.c, src/segments.h, + src/supersegments.c, src/types.h, src/filedumper.c, src/nodes.c: + Sort the nodes geographically and take coordinates as command line arguments. + +2009-02-02 Andrew M. Bishop + + * src/ways.c, src/ways.h, src/nodes.c, src/nodes.h, src/osmparser.c, src/segments.c, + src/segments.h, src/supersegments.c, src/types.h: + More variable and function name changes. + +2009-02-01 Andrew M. Bishop + + * src/profiles.c, src/router.c, src/segments.c, src/segments.h, src/supersegments.c, + src/ways.c, src/ways.h, src/files.c, src/functions.h, src/nodes.c, src/nodes.h, + src/optimiser.c, src/osmparser.c, src/planetsplitter.c, src/filedumper.c: + Rename some variable types. + +2009-01-31 Andrew M. Bishop + + * src/segments.c, src/segments.h, src/supersegments.c, src/types.h, src/ways.c, src/ways.h, + src/functions.h, src/nodes.c, src/nodes.h, src/optimiser.c, src/planetsplitter.c, + src/profiles.h, src/router.c: + Intermediate version during code cleanup. + + * src/optimiser.c, src/planetsplitter.c, src/router.c, src/segments.c, src/segments.h, + src/functions.h, src/nodes.h: + Intermediate checkin, routing now working. + + * src/Makefile: + Don't print out anything when creating the dependencies directory. + + * src/planetsplitter.c, src/router.c: + Add command line options to specify the directory and filename prefix. + +2009-01-30 Andrew M. Bishop + + * src/results.c, src/planetsplitter.c: Remove gcc warning. + + * src/Makefile: Move dependencies to subdir. + + * src/osmparser.c: Remove gcc warning. + +2009-01-29 Andrew M. Bishop + + * src/functions.h, src/nodes.c, src/nodes.h, src/optimiser.c, src/planetsplitter.c, + src/router.c, src/segments.c, src/segments.h, src/supersegments.c: + Intermediate version while transitioning data format for nodes and segments. + +2009-01-28 Andrew M. Bishop + + * src/Makefile, src/functions.h, src/nodes.c, src/nodes.h, src/optimiser.c, src/osmparser.c, + src/planetsplitter.c, src/segments.c, src/segments.h, src/supersegments.c, src/ways.c, + src/ways.h: + Intermediate version while transitioning data format for nodes and segments. + +2009-01-27 Andrew M. Bishop + + * src/Makefile, src/functions.h, src/nodes.c, src/nodes.h, src/optimiser.c, + src/planetsplitter.c, src/router.c, src/segments.c, src/segments.h, src/supersegments.c, + src/ways.c, src/ways.h: + Intermediate version while transitioning data format for nodes and segments. + +2009-01-26 Andrew M. Bishop + + * src/osmparser.c, src/planetsplitter.c, src/segments.c, src/segments.h, + src/supersegments.c, src/ways.c, src/ways.h, src/filedumper.c, src/files.c, src/functions.h, + src/optimiser.c: + Change Segment to contain index of way not its real ID. + Don't store the real way ID to save space. + +2009-01-25 Andrew M. Bishop + + * src/segments.c, src/segments.h: + Slightly speed up the Duration calculation by changing the macro. + + * src/osmparser.c, src/profiles.c, src/ways.c, src/ways.h: + Fix misspelling of Unclassified. + + * src/planetsplitter.c, src/segments.c, src/segments.h, src/supersegments.c, src/ways.h, + src/optimiser.c: + Change the segment->way so that it contains the index of the way, not the id. + + * src/profiles.c, src/profiles.h: New file. + + * src/ways.c, src/ways.h, src/Makefile, src/functions.h, src/optimiser.c, src/osmparser.c, + src/planetsplitter.c, src/router.c, src/segments.c, src/segments.h: + Added profiles to define speed and allowed highways. + Added new options to planetsplitter and router to use the profiles. + +2009-01-24 Andrew M. Bishop + + * src/optimiser.c: Changed some variable names for clarity. + + * src/planetsplitter.c: Print more information about progress. + Don't quit until 99.9% unchanged. + + * src/optimiser.c, src/results.c, src/results.h, src/supersegments.c: + Change the Results structure so that the real data doesn't need to be realloc(). + Add functions to access the first and subsequent elements of the Results structure. + +2009-01-23 Andrew M. Bishop + + * src/osmparser.c, src/planetsplitter.c: + Fix bug with not specifying a method of transport. + + * src/optimiser.c, src/router.c: Proper check that it was unroutable. + + * src/functions.h, src/optimiser.c, src/planetsplitter.c, src/supersegments.c: + Remove "iteration" as function argument. + + * src/functions.h, src/optimiser.c, src/osmparser.c, src/planetsplitter.c, src/router.c, + src/ways.c, src/ways.h: + Add command line options to planetsplitter and router. + Select transport type (must be allowed on way for parsing). + Select highway types (ignore when parsing or routing). + + * src/ways.h, src/functions.h, src/optimiser.c, src/osmparser.c, src/router.c, + src/segments.c, src/segments.h, src/ways.c: + Add enumerated type Transport. + Replace variables of AllowType with Transport where more appropriate. + Replace AllowType with Allowed. + Replace WayType with Highway. + + * src/osmparser.c: Only include ways that are not Way_Unknown type. + + * src/osmparser.c: Include permissive access. + + * src/functions.h, src/optimiser.c, src/results.c, src/results.h, src/router.c: + Create a large or small results structure depending on how many nodes are + expected. + +2009-01-22 Andrew M. Bishop + + * src/results.h: Increase the number of bins to 64k. + + * src/optimiser.c, src/osmparser.c, src/segments.c, src/segments.h, src/supersegments.c: + Remove INVALID_DISTANCE and INVALID_DURATION. + + * src/optimiser.c, src/osmparser.c, src/supersegments.c, src/ways.c, src/ways.h: + Removed the Way_TYPE() macro. + + * src/results.c, src/results.h, src/optimiser.c: + Move queue functions into results.c. + + * src/filedumper.c, src/nodes.c, src/nodes.h, src/planetsplitter.c, src/router.c: + Nodes, Segments, Ways - Nodes, Segments, Ways. + + * src/filedumper.c, src/nodes.c, src/nodes.h, src/segments.c, src/segments.h, src/ways.c, + src/ways.h: + Remove the choice of indexed or non-indexed data structures. + +2009-01-21 Andrew M. Bishop + + * src/optimiser.c: + Various small speed-ups including not-reversing direction. + + * src/functions.h, src/optimiser.c, src/osmparser.c, src/router.c, src/segments.c, + src/segments.h, src/supersegments.c, src/ways.c, src/ways.h: + Calculate way speeds at routing time. + + * src/supersegments.c: + Add reverse-oneway segments when creating supernodes. + Check incoming oneway streets as well as outgoing ones. + + * src/osmparser.c: Don't change speed on roundabouts. + +2009-01-20 Andrew M. Bishop + + * src/planetsplitter.c: + Add command line options for skipping parsing and iteration limit. + + * src/optimiser.c, src/osmparser.c, src/segments.c, src/segments.h, src/supersegments.c: + Remove duration from segment, calculate duration depending on speed. + +2009-01-19 Andrew M. Bishop + + * src/functions.h, src/optimiser.c, src/planetsplitter.c, src/supersegments.c: + Iteratively calculate the super-segments. + + * src/ways.h: Redefine Way_TYPE() to include one-way status. + +2009-01-18 Andrew M. Bishop + + * src/optimiser.c, src/supersegments.c: + Fix problems with way-type matching and duplicated/missing super-segments. + + * src/functions.h, src/optimiser.c, src/router.c: Print out a GPX file. + + * src/optimiser.c, src/filedumper.c, src/functions.h, src/planetsplitter.c, src/router.c, + src/segments.c, src/segments.h, src/supersegments.c, src/ways.c, src/ways.h: + Added Super-Ways and allow user to select method of transport. + + * src/segments.c: Fix for changes made to ways. + + * src/supersegments.c: + Ensure that supernodes are inserted wherever the way type changes. + + * src/osmparser.c: Fill in the extra way information. + + * src/ways.h: + Store more information about a way (allowed modes of transport). + + * src/filedumper.c: Fix output printing. + + * src/router.c: Print an error if no route can be found. + + * src/optimiser.c: + Fix bugs when start and/or finish nodes are supernodes. + +2009-01-17 Andrew M. Bishop + + * src/Makefile: Add the option to create assembler output files. + + * src/optimiser.c, src/results.c, src/results.h, src/supersegments.c: + Change the contents of the results data structure. + + * src/router.c: Added an option to not print the result. + +2009-01-16 Andrew M. Bishop + + * src/optimiser.c, src/results.h, src/router.c: + Speed optimisation by changing the contents of the Results structure. + + * src/optimiser.c: + Don't bother calculating the distance to go, it takes too long. + +2009-01-14 Andrew M. Bishop + + * src/planetsplitter.c: Remove bad segments and non-way nodes. + + * src/nodes.c, src/nodes.h: Remove nodes which are not in highways. + Fix the sorting and create indexes after sorting, not before saving. + + * src/segments.c, src/segments.h: + Remove bad segments (repeated consecutive nodes and duplicate segments). + Fix the sorting and create indexes after sorting, not before saving. + + * src/supersegments.c: Use invalid distances properly. + + * src/ways.c: + Fix the sort algorithm and update the indexes after sorting, not before saving. + + * src/optimiser.c: Fix the bug with merging the results. + Fix the bug with not clearing the results structure properly. + + * src/osmparser.c: + Add segments that correspond to the wrong way along one-way routes with an + invalid distance. + +2009-01-11 Andrew M. Bishop + + * src/functions.h, src/optimiser.c, src/router.c: + Routes correctly using super-nodes (not Lands End to John O'Groats though). + + * src/filedumper.c, src/functions.h, src/optimiser.c, src/planetsplitter.c, src/router.c, + src/segments.h, src/supersegments.c: + Replace Junction with SuperNode. + + * src/nodes.c, src/nodes.h, src/segments.h, src/ways.c, src/ways.h: + Some small changes to the nodes, segments and ways functions. + + * src/Makefile, src/filedumper.c, src/functions.h, src/optimiser.c, src/planetsplitter.c, + src/results.h, src/router.c, src/segments.c, src/segments.h, src/supersegments.c: + Working version with supersegments and junctions. + +2009-01-10 Andrew M. Bishop + + * src/ways.c, src/ways.h, src/osmparser.c, src/segments.c: + Store more information about ways. + + * src/results.h, src/results.c: New file. + + * src/Makefile, src/optimiser.c: + Move the results data type into new files. + + * src/nodes.h, src/segments.h, src/ways.h: + Increase the increment for the indexed array case. + + * src/ways.h, src/Makefile, src/filedumper.c, src/functions.h, src/nodes.c, src/nodes.h, + src/optimiser.c, src/osmparser.c, src/planetsplitter.c, src/router.c, src/segments.c, + src/segments.h, src/supersegments.c, src/ways.c: + About to add the super-segment functionality using Segments data type to hold + them. + + * src/functions.h, src/types.h: + Changed after nodes, ways and segment changes. + +2009-01-09 Andrew M. Bishop + + * src/segments.h: New file. + + * src/segments.c: + Changed the format of the segments data type to match the nodes. + + * src/nodes.h: Enable indexed arrays. + + * src/ways.h: New file. + + * src/ways.c: + Changed the format of the ways data type to match the nodes. + + * src/nodes.c, src/nodes.h: + Changed the format of the nodes data type again. + +2009-01-07 Andrew M. Bishop + + * src/nodes.h: New file. + + * src/nodes.c: Lots of modifications: + Two data structures - in memory (pointers) and in file (array). + Data is hashed into multiple bins. + Each function takes a nodes structure as an argument. + +2009-01-06 Andrew M. Bishop + + * src/supersegments.c: New file. + + * src/Makefile, src/filedumper.c, src/functions.h, src/planetsplitter.c, src/types.h: + Added SuperSegments data type, but it does nothing yet. + + * src/optimiser.c: + Tried to optimise the Queue data type. It was slower than the original. + +2009-01-05 Andrew M. Bishop + + * src/filedumper.c: Print out the longest segment. + + * src/optimiser.c: + Some optimisations. Increase the number of result bins and change + find_insert_result() into insert_result(). + +2009-01-04 Andrew M. Bishop + + * src/optimiser.c: Introduced some new data types to simplify the code. + + * src/filedumper.c: Print more useful information. + + * src/segments.c, src/types.h, src/ways.c, src/filedumper.c, src/functions.h, src/nodes.c, + src/optimiser.c, src/osmparser.c, src/planetsplitter.c: + Changed the node, way and segment functions and data types. + Removed 'alloced', shortened the prototype array. + Remove the automatic sorting of the data. + Added assert statements. + +2009-01-03 Andrew M. Bishop + + * src/ways.c: New file. + + * src/router.c, src/types.h, src/Makefile, src/filedumper.c, src/functions.h, + src/optimiser.c, src/osmparser.c, src/planetsplitter.c: + Added the ways to the output. + +2009-01-02 Andrew M. Bishop + + * src/optimiser.c, src/osmparser.c, src/segments.c, src/types.h: + Added macros to convert between distance/km and duration/hours/minutes. + Shortened the Segment data type with shorter distances and durations. + +2009-01-01 Andrew M. Bishop + + * src/functions.h, src/nodes.c, src/planetsplitter.c, src/segments.c, src/types.h: + Remove the functions to initialise the node and segment arrays. + + * src/optimiser.c, src/router.c, src/Makefile: Print out the results. + +2008-12-31 Andrew M. Bishop + + * src/types.h, src/segments.c, src/router.c, src/planetsplitter.c, src/osmparser.c, + src/optimiser.c, src/nodes.c, src/functions.h, src/files.c, src/filedumper.c, src/Makefile: + New file. + diff --git a/INSTALL.txt b/INSTALL.txt new file mode 120000 index 0000000..b0385da --- /dev/null +++ b/INSTALL.txt @@ -0,0 +1 @@ +doc/INSTALL.txt \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..b6ee933 --- /dev/null +++ b/Makefile @@ -0,0 +1,45 @@ +# $Header: /home/amb/routino/RCS/Makefile,v 1.3 2010/05/31 12:44:43 amb Exp $ +# +# Makefile +# +# Part of the Routino routing software. +# +# This file Copyright 2009-2010 Andrew M. Bishop +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# + +FILES=$(wildcard */Makefile) +DIRS=$(foreach f,$(FILES),$(dir $f)) + +######## + +all: + for dir in $(DIRS); do \ + ( cd $$dir && $(MAKE) $@ ); \ + done + +######## + +clean: + for dir in $(DIRS); do \ + ( cd $$dir && $(MAKE) $@ ); \ + done + +######## + +distclean: clean + for dir in $(DIRS); do \ + ( cd $$dir && $(MAKE) $@ ); \ + done diff --git a/NEWS.txt b/NEWS.txt new file mode 120000 index 0000000..7a887e7 --- /dev/null +++ b/NEWS.txt @@ -0,0 +1 @@ +doc/NEWS.txt \ No newline at end of file diff --git a/README.txt b/README.txt new file mode 120000 index 0000000..253de0c --- /dev/null +++ b/README.txt @@ -0,0 +1 @@ +doc/README.txt \ No newline at end of file diff --git a/agpl-3.0.txt b/agpl-3.0.txt new file mode 100644 index 0000000..dba13ed --- /dev/null +++ b/agpl-3.0.txt @@ -0,0 +1,661 @@ + GNU AFFERO GENERAL PUBLIC LICENSE + Version 3, 19 November 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU Affero General Public License is a free, copyleft license for +software and other kinds of works, specifically designed to ensure +cooperation with the community in the case of network server software. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +our General Public Licenses are intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + Developers that use our General Public Licenses protect your rights +with two steps: (1) assert copyright on the software, and (2) offer +you this License which gives you legal permission to copy, distribute +and/or modify the software. + + A secondary benefit of defending all users' freedom is that +improvements made in alternate versions of the program, if they +receive widespread use, become available for other developers to +incorporate. Many developers of free software are heartened and +encouraged by the resulting cooperation. However, in the case of +software used on network servers, this result may fail to come about. +The GNU General Public License permits making a modified version and +letting the public access it on a server without ever releasing its +source code to the public. + + The GNU Affero General Public License is designed specifically to +ensure that, in such cases, the modified source code becomes available +to the community. It requires the operator of a network server to +provide the source code of the modified version running there to the +users of that server. Therefore, public use of a modified version, on +a publicly accessible server, gives the public access to the source +code of the modified version. + + An older license, called the Affero General Public License and +published by Affero, was designed to accomplish similar goals. This is +a different license, not a version of the Affero GPL, but Affero has +released a new version of the Affero GPL which permits relicensing under +this license. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU Affero General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Remote Network Interaction; Use with the GNU General Public License. + + Notwithstanding any other provision of this License, if you modify the +Program, your modified version must prominently offer all users +interacting with it remotely through a computer network (if your version +supports such interaction) an opportunity to receive the Corresponding +Source of your version by providing access to the Corresponding Source +from a network server at no charge, through some standard or customary +means of facilitating copying of software. This Corresponding Source +shall include the Corresponding Source for any work covered by version 3 +of the GNU General Public License that is incorporated pursuant to the +following paragraph. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the work with which it is combined will remain governed by version +3 of the GNU General Public License. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU Affero General Public License from time to time. Such new versions +will be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU Affero General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU Affero General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU Affero General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If your software can interact with users remotely through a computer +network, you should also make sure that it provides a way for users to +get its source. For example, if your program is a web application, its +interface could display a "Source" link that leads users to an archive +of the code. There are many ways you could offer source, and different +solutions will be better for different programs; see section 13 for the +specific requirements. + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU AGPL, see +. diff --git a/doc/ALGORITHM.txt b/doc/ALGORITHM.txt new file mode 100644 index 0000000..2432a88 --- /dev/null +++ b/doc/ALGORITHM.txt @@ -0,0 +1,212 @@ + Routino : Algorithm + =================== + + + This page describes the development of the algorithm that is used in + Routino for finding routes. + + +Simplest Algorithm +------------------ + + The algorithm to find a route is fundamentally simple: Start at the + beginning, follow all possible routes and keep going until you reach + the end. + + While this method does work, it isn't fast. To be able to find a route + quickly needs a different algorithm, one that can find the correct + answer without wasting time on routes that lead nowhere. + + +Improved Algorithm +------------------ + + The simplest way to do this is to follow all possible segments from the + starting node to the next nearest node (an intermediate node in the + complete journey). For each node that is reached store the shortest + route from the starting node and the length of that route. The list of + intermediate nodes needs to be maintained in order of shortest overall + route on the assumption that there is a straight line route from here + to the end node. + At each point the intermediate node that has the shortest potential + overall journey time is processed before any other node. From the first + node in the list follow all possible segments and place the newly + discovered nodes into the same list ordered in the same way. This will + tend to constrain the list of nodes examined to be the ones that are + between the start and end nodes. If at any point you reach a node that + has already been reached by a longer route then you can discard that + route since the newly discovered route is shorter. Conversely if the + previously discovered route is shorter then discard the new route. + At some point the end node will be reached and then any routes with + potential lengths longer than this actual route can be immediately + discarded. The few remaining potential routes must be continued until + they are found to be shorter or have no possibility of being shorter. + The shortest possible route is then found. + + At all times when looking at a node only those segments that are + possible by the chosen means of transport are followed. This allows the + type of transport to be handled easily. When finding the quickest route + the same rules apply except that criterion for sorting is the shortest + potential route (assuming that from each node to the end is the fastest + possible type of highway). + + This method also works, but again it isn't very fast. The problem is + that the complexity is proportional to the number of nodes or segments + in all routes examined between the start and end nodes. Maintaining the + list of intermediate nodes in order is the most complex part. + + +Final Algorithm +--------------- + + The final algorithm that is implemented in the router is basically the + one above but with an important difference. Instead of finding a long + route among a data set of 8,000,000 nodes (number of highway nodes in + UK at beginning of 2010) it finds one long route in a data set of + 1,000,000 nodes and a few hundred very short routes in the full data + set. Since the time taken to find a route is proportional to the number + of nodes the main route takes 1/10th of the time and the very short + routes take almost no time at all. + + The solution to making the algorithm fast is therefore to discard most + of the nodes and only keep the interesting ones. In this case a node is + deemed to be interesting if it is the junction of two segments with + different properties. In the algorithm these are classed as + super-nodes. Starting at each super-node a super-segment is generated + that finishes on another super-node and contains the shortest path + along segments with identical properties (and these properties are + inherited by the super-segment). The point of choosing the shortest + route is that since all segments considered have identical properties + they will be treated identically when properties are taken into + account. This decision making process can be repeated until the only + the most important and interesting nodes remain. + + To find a route between a start and finish point now comprises the + following steps (assuming a shortest route is required): + + 1. Find all shortest routes from the start point along normal segments + and stopping when super-nodes are reached. + 2. Find all shortest routes from the end point backwards along normal + segments and stopping when super-nodes are reached. + 3. Find the shortest route along super-segments from the set of + super-nodes in step 1 to the set of super-nodes in step 2 (taking + into account the lengths found in steps 1 and 2 between the + start/finish super-nodes and the ultimate start/finish point). + 4. For each super-segment in step 3 find the shortest route between + the two end-point super-nodes. + + This multi-step process is considerably quicker than using all nodes + but gives a result that still contains the full list of nodes that are + visited. There are some special cases though, for example very short + routes that do not pass through any super-nodes, or routes that start + or finish on a super-node. In these cases one or more of the steps + listed can be removed or simplified. + +Routing Preferences + + One of the important features of Routino is the ability to select a + route that is optimum for a set of criteria such as preferences for + each type of highway, speed limits and other restrictions and highway + properties. + + All of these features are handled by assigning a score to each segment + while calculating the route and trying to minimise the score rather + than simply minimising the length. + + Segment length + When calculating the shortest route the length of the segment is + the starting point for the score. + + Speed preference + When calculating the quickest route the time taken calculated + from the length of the segment and the lower of the highway's + own speed limit and the user's speed preference for the type of + highway is the starting point for the score. + + Oneway restriction + If a highway has the oneway property in the opposite direction + to the desired travel and the user's preference is to obey + oneway restrictions then the segment is ignored. + + Weight, height, width & length limits + If a highway has one of these limits and its value is less than + the user's specified requirement then the segment is ignored. + + Highway preference + The highway preference specified by the user is a percentage, + these are scaled so that the most preferred highway type has a + weighted preference of 1.0 (0% always has a weighted preference + of 0.0). The calculated score for a segment is divided by this + weighted preference. + + Highway properties + The other highway properties are specified by the user as a + percentage and each highway either has that property or not. The + user's property preference is scaled into the range 0.0 (for 0%) + to 2.0 (for 100%) to give a weighted preference, a second + "non-property" weighted preference is calcuated in the same way + after subtracting the user's preference from 100%. If a segment + has this property then the calculated score is divided by the + weighted preference, if the segment does not have this property + then it is divided by the non-property weighted preference. + +Implementation +-------------- + + The hardest part of implementing this router is the data organisation. + The arrangement of the data to minimise the number of operations + required to follow a route from one node to another is much harder than + designing the algorithm itself. + + The final implementation uses a separate table for nodes, segments and + ways. Each table individually is implemented as a C-language data + structure that is written to disk by a program which parses the + OpenStreetMap XML data file. In the router these data structures are + memory mapped so that the operating system handles the problems of + loading the needed data blocks from disk. + + Each node contains a latitude and longitude and they are sorted + geographically so that converting a latitude and longitude coordinate + to a node is fast as well as looking up the coordinate of a node. The + node also contains the location in the array of segments for the first + segment that uses that node. + Each segment contains the location of the two nodes as well as the way + that the segment came from. The location of the next segment that uses + one of the two nodes is also stored; the next segment for the other + node is the following one in the array. The length of the segment is + also pre-computed and stored. + Each way has a name, a highway type, a list of allowed types of + traffic, a speed limit, any weight, height, width or length + restrictions and the highway properties. + + The super-nodes are mixed in with the nodes and the super-segments are + mixed in with the segments. For the nodes they are the same as the + normal nodes, so just a flag is needed to indicate that they are super. + The super-segments are in addition to the normal segments so they + increase the database size (by about 10%) and are also marked with a + flag. + + +Practicalities +-------------- + + At the time of writing (April 2010) the OpenStreetMap data for Great + Britain (taken from GeoFabrik) contains: + * 14,675,098 nodes + + 8,767,521 are highway nodes + + 1,120,297 are super-nodes + * 1,876,822 ways + + 1,412,898 are highways + o 9,316,328 highway segments + o 1,641,009 are super-segments + * 60,572 relations + + The database files when generated are 41.5 MB for nodes, 121.6 MB for + segments and 12.6 MB for ways and are stored uncompressed. By having at + least 200 MB or RAM available the routing can be performed with no disk + accesses (once the data has been read once). + + +-------- + +Copyright 2008-2010 Andrew M. Bishop. diff --git a/doc/CONFIGURATION.txt b/doc/CONFIGURATION.txt new file mode 100644 index 0000000..fa6ae48 --- /dev/null +++ b/doc/CONFIGURATION.txt @@ -0,0 +1,177 @@ + Routino : Configuration + ======================= + + + New in version 1.4 of Routino is the use of configuration files to + allow more information to be provided to the programs at run-time. The + configuration files that are used are: + * Tagging transformation rules for the planetsplitter program. + * Routing profiles for the router program. + * Output translations for the router program. + + In keeping with the nature of the input and output files the + configuration files are also XML files. Each of the files uses a custom + defined XML schema and an XSD file is provided for each of them. + + +Tag Transformation Rules +------------------------ + + The default name of the tagging transformation rules XML configuration + file is tagging.xml in the same directory as the generated database + files. Other filenames can be specified on the command line using the + --tagging option. When processing the input it is possible to have a + different set of tagging rules for each file; for example different + rules for different countries. + + The tagging rules allow modifying the highway tags in the source file + so that the routing can be performed on a simpler set of tags. This + removes the special case tagging rules from the source code into the + configuration file where they can be easily modified. Part of the + provided tagging.xml file showing the rules for motorway_link and + motorway highway types. + + + + + + + + + + + + + + + + + + + + + + + +... + + + + + The rules all have the same format; an if element for matching the + input and some set or output elements to either change the input tags + or create an output tag. The k and v attributes have the same meaning + as the attributes with the same names in the OSM XML file - the tag key + and tag value. + + An if rule that has both k and v specified is only applied if a tag + exists in the input that matches both. An if rule that has only the k + attribute is applied if a tag with that key exists and an if rule that + has only the v attribute is applied to all tags with that value. + + For the set and output elements the tag that is created in the input or + output tag set uses the k and v attributes specified. If one or both + are not specified then the original ones are used. + + +Routing Profiles +---------------- + + The default name of the routing profiles XML configuration file is + profiles.xml in the same directory as the database files. Other + filenames can be specified on the command line using the --tagging + option. + + The purpose of this configuration file is to allow easy modification of + the routing parameters so that they do not all need to be specified on + the command line. In versions of Routino before version 1.4 the default + routing parameters (preferred highways, preferred speeds etc) were + contained in the source code, now they are in a configuration file. + When calculating a route the --profile option selects the named profile + from the configuration file. + + Part of the provided profiles.xml file showing the parameters for + transport on foot is shown below: + + + + + + +... + + + + + +... + + + + + + + + + + + + + + + + + + + +... + +... + + + +Output Translations +------------------- + + The default name of the output translations XML configuration file is + translations.xml in the same directory as the database files. Other + filenames can be specified on the command line using the --translations + option. + + The generated HTML and GPX output files (described in the next section) + are created using the fragments of text that are defined in this file. + Additional languages can be added to the file and are selected using + the --language option to the router. If no language is specified the + first one in the file is used. + + Part of the provided translations.xml file showing some of the English + language (en) translations is shown below: + + + + + +... + + + +... + + + +... + + + +... + + +... + + + + + +-------- + +Copyright 2010 Andrew M. Bishop. diff --git a/doc/DATA.txt b/doc/DATA.txt new file mode 100644 index 0000000..8cd61ed --- /dev/null +++ b/doc/DATA.txt @@ -0,0 +1,115 @@ + Routino : Data + ============== + + + A router relies on data to be able to find a route. + + +OpenStreetMap Data +------------------ + + The data that is collected by the OpenStreetMap project consists of + nodes, ways and relations. + + Node + A node is a point that has a latitude and longitude and + attributes that describe what type of point it is (part of a way + or a place of interest for example). + + Way + A way is a collection of nodes that when joined together define + something (for example a road, a ralway, a boundary, a building, + a lake etc). The ways also have attributes that define them + (speed limits, type of road and restrictions for example). + + Relation + A relation is a collection of items (usually ways) that are + related to each other for some reason (highways that make up a + route for example). + + The OpenStreetMap Wiki explains the data much better than I can. + + +Router Data +----------- + + The information that is needed by a routing algorithm is only a subset + of the information that is collected by the OpenStreetMap project. For + routing what is required is information about the location of roads (or + other highways), the connections between the highways and the + properties of those highways. + + Location of highways (nodes) + The locations of things is provided by the nodes from the + OpenStreetMap data. The nodes are the only things that have + coordinates in OpenStreetMap and everything else is made up by + reference to them. Not all of the nodes are useful, only the + ones that are part of highways. The location of the nodes is + stored but none of the other attributes are currently used by + the router. + + Location of highways (ways) + The location of the highways is defined in the OpenStreetMap + data by the ways. Only the highway ways are useful and the other + ways are discarded. What remains is lists of nodes that join + together to form a section of highway. This is further split + into segments which are individual parts of a way connected by + two nodes. + + Properties of highways (tags) + The ways that belong to highways are extracted from the data in + the previous step and for each way the useful information for + routing is stored. For the router the useful information is the + type of highway, the speed limit, the allowed types of transport + and other restrictions (one-way, min height, max weight etc). + + Connections between highways + The connections between highways are defined in the + OpenStreetMap data by ways that share nodes. Since the ways may + join in the middle and not just the ends it is the segments + defined above that are not part of the OpenStreetMap data that + are most important. + + The information that is extracted from the OpenStreetMap data is stored + in an optimised way that allows the routing to be performed quickly. + + +Interpreting Data Tags +---------------------- + + The tags are the information that is attached to the nodes and ways in + OpenStreetMap. The router needs to interpret these tags and use them + when deciding what type of traffic can use a highway (for example). + + There are no well defined rules in OpenStreetMap about tagging, but + there is guidance on the OpenStreetMap Wiki "Map_Features" page. This + describes a set of recommended tags but these are not universally used + so it is up to each application how to interpret them. + + The tagging rules that the router uses are very important in + controlling how the router works. With Routino the data tags can be + modified when the data is imported to allow customisation of the + information used for routing. + + +Problems With OpenStreetMap Data +-------------------------------- + + The route that can be found is only as good as the data that is + available. This is not intended as a criticism of the OpenStreetMap + data; it is generally good. + + There are some problems that are well known and which affect the + router. For example highways might be missing because nobody has mapped + them. A highway may be wrongly tagged with incorrect properties, or a + highway might be missing important tags for routing (e.g. speed + limits). There can also be problems with highways that should join but + don't because they do not share nodes. + + A lot of these problems can be found using the interactive data + visualiser that uses the same Routino rouing database. + + +-------- + +Copyright 2008-2010 Andrew M. Bishop. diff --git a/doc/INSTALL.txt b/doc/INSTALL.txt new file mode 100644 index 0000000..123f02c --- /dev/null +++ b/doc/INSTALL.txt @@ -0,0 +1,158 @@ + Routino : Installation + ====================== + + +Compilation +----------- + + This program has been written to run on Linux, no cross-platform + compatibility has been specifically included but on the other hand + nothing platform specific has been knowingly included either. + + Any information on improving the compilation process on anything other + than 32-bit x86 Linux is welcome. + + No external libraries are required and the programs are written in + standard C language. + + To compile the programs just type 'make'. + + +Installation +------------ + + After compilation the executable files are copied into the directory + web/bin and the default XML configuration files are copied into the + directory web/data. This is in preparation for using the supplied + example web pages but is also a useful location to copy the files from + for normal use. + + The executable files are called 'planetsplitter', 'router' and 'filedumper' + (also 'tagmodifier' for debugging tag modifications). They can be copied + to any location and need no special installation environment. + + The default configuration files are called 'profiles.xml', 'tagging.xml' + and 'translations.xml'. The names of the configuration files can be + specified on the command line but by default are also looked for in the + directory that contains the routing database. + + +Example Web Page +---------------- + + The directory 'web' contains a set of files that can be used to create a + working set of web pages with interfaces to the routing algorithm. + + The files in the 'web' directory will require copying to a location that + is accessible by a web server. After copying the files some of them + need to be edited; search through the files for lines that contain the + words "EDIT THIS" and make appropriate edits. The files that need + editing are 'paths.pl' (to set the directory paths) and 'router.js' and + 'visualiser.js' to limit the range of the visible map (latitude, + longitude and zoom). + + +Configuration of web files +-------------------------- + + The assumption in this description is that the whole of the directory + called web is copied into a directory that is accessible by an Apache + web server. + + ************************************************************************** + **** This is not a secure configuration but an easy one to configure. **** + **** Only the directory 'www' should be accessible by the web server. **** + **** Do not use this configuration unmodified in a public web server. **** + ************************************************************************** + + The directory structure is as follows: + + web/ + | + + /bin/ <- The Routino executable files (when compiled). + | + + /data/ <- The Routino database and default configuration + | files. + | + + /results/ <- An empty directory to store the results. + | + + /www/ <- The files that must be available to the web + | server are below this level. + | + + /openlayers/ <- A directory to hold the OpenLayers scripts. + | + + /routino/ <- The main HTML, Javascript, CSS and CGI files. + + + The directory 'bin' will be filled by running the compilation process. + For a secure installation the 'bin' directory should be outside of the + web server, the file 'www/routino/paths.pl' contains the path to the 'bin' + directory. + + The directory 'data' must contain the Routino database and is also the + default location for the configuration files. The routing database is + created by downloading the OSM files for the region of interest and + running the 'planetsplitter' program. There is a script in the directory + that will download the OSM files and create the required database. The + script should be edited to set the names of the files to be downloaded. + For a secure installation the 'data' directory should be outside of the + web server, the file 'www/routino/paths.pl' contains the path to the 'data' + directory. + + The directory 'results' is a temporary directory that it used to hold the + GPX and text files generated by the Routino router. The directory must + be writable by the web server process since it is the CGI scripts that + are run by the web server that writes the results here. For a secure + installation the results directory should be outside of the web server, + the file 'www/routino/paths.pl' contains the path to the results + directory. + + The directory 'www' and its sub-directories are the only ones that need + to be within the web server accessible directory. + + The directory 'www/openlayers' must be filled with the openlayers + Javascript library that can be downloaded from + http://www.openlayers.org/. (This version of Routino has been tested + with OpenLayers library version 2.8). The files must be installed so + that the file 'www/openlayers/OpenLayers.js' and the directories + 'www/openlayers/img/', 'www/openlayers/theme/' all exist. There is a script + in the directory that will automatically download and organise the + files. + + The directory 'www/routino' contains the main HTML, Javascript and CSS + files as well as the CGI scripts that perform the server-side + functions. The description below lists all of the files that contain + editable information. + + paths.pl + This contains the names of the directories that contain the + executable files, router database and temporary results. + + router.pl + This file contains the filename prefix for the routing database + files (only needed if planetsplitter is run with the --prefix + option). + + router.js + The parameters in this file control the boundary of the visible + map (defaults to UK), the minimum and maximum zoom levels + (defaults to between 4 and 15 inclusive) and the source of map + tiles (defaults to the main OpenStreetMap tile server). + + visualiser.js + The same parameters as in router.js are in this file. + + +Configuration of web server +--------------------------- + + The file 'www/routino/.htaccess' contains all of the Apache configuration + options that are required to get the example web pages running. The + only problem is that because of the way that the "AllowOverride" option + works one of the configuration options has been commented out. This + must be enabled in the main Apache server configuration file. + + +-------- + +Copyright 2008-2010 Andrew M. Bishop. diff --git a/doc/Makefile b/doc/Makefile new file mode 100644 index 0000000..5bc7047 --- /dev/null +++ b/doc/Makefile @@ -0,0 +1,48 @@ +# $Header: /home/amb/routino/doc/RCS/Makefile,v 1.2 2010/07/07 17:27:03 amb Exp $ +# +# Documentation directory Makefile +# +# Part of the Routino routing software. +# +# This file Copyright 2010 Andrew M. Bishop +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# + +WEBDIR=../web/www/routino/documentation + +FILES=html/* + +######## + +all : + -@[ -d $(WEBDIR) ] && \ + for file in $(FILES); do \ + if [ ! -f $(WEBDIR)/`basename $$file` ] || [ $$file -nt $(WEBDIR)/`basename $$file` ]; then \ + echo cp $$file $(WEBDIR) ;\ + cp -f $$file $(WEBDIR) ;\ + fi ;\ + done + +######## + +clean: + rm -f *~ + rm -f html/*~ + rm -f $(WEBDIR)/*~ + +######## + +distclean: clean + rm -f $(WEBDIR)/* diff --git a/doc/NEWS.txt b/doc/NEWS.txt new file mode 100644 index 0000000..a003671 --- /dev/null +++ b/doc/NEWS.txt @@ -0,0 +1,177 @@ +Version 1.4.1 of Routino released : Sat Jul 10 2010 +--------------------------------------------------- + +Bug fixes: + Don't crash if start and finish are the same point. + Don't crash if several translations but --language option not used. + Don't crash if middle part of route cannot be found. + Don't allocate so much memory for intermediate nodes; routes much faster. + Fix problem with finding closest segment to the specified point. + +Documentation: + Provide HTML versions of the documentation (copy to web directory at install). + Change URL for website to http://www.routino.org/. + +Configuration Files: + Added German translations. + +planetsplitter + Slight change to algorithm for finding super-nodes. + +Web pages: + Provide HTML versions of the documentation. + Change URL for website to http://www.routino.org/. + Provide updated HTML files, the same as on the website. + Change to OpenLayers v2.9.1 and build custom version if Python available. + + +Version 1.4 of Routino released : Mon May 31 2010 +------------------------------------------------- + +Bug fixes: + Speed up start/via/stop point within segment search algorithm. + If no segment is found don't try routing but exit with error. + Improve the error messages by adding operating system error info to them. + Rewrite of tagging rules fixes bug with wheelchair access allow/deny. + Files greater than 2GB can be read/written on 32-bit systems. + Fix bug with profile preferences when optimising a route. + Stricter check on profile validity before starting routing. + +planetsplitter + Add --parse-only and --process-only options (for incremental parsing). + Allow filenames to be specified on command line (default is still stdin). + Improved the '--help' information to describe all options. + Remove --transport, --not-highway, --not-property options (use config file). + Use tag transformation rules in configuration file not hard-coded. + +router + Removed compiled-in profiles and use profiles loaded from XML file. + Improved the '--help' information to describe all options. + Change the name of the --profile-json and --profile-perl options. + Allow selection of the outputs to generate (or none). + Added HTML route instructions output. + GPX route file contains instructions at each waypoint. + Read in XML file of translated words/phrases for outputs. + Added options to specify file of translations and language to use. + Remove copyright.txt file and put information into translations file. + +filedumper + Improved the '--help' information to describe all options. + Added the option to dump an OSM file containing database contents. + +Web Pages + Combined generic map CSS into one file (not copied in two). + Much better support for IE6/7/8 with browser detection but not perfect. + Re-organised and tidied up the Javascript. + Added button next to waypoints to centre it on map. + Added button next to waypoints to set as home location (uses browsser cookie). + Create shorter URLs for custom map (ignore default values). + Reduced and clarified the amount of editing to customise the Javascript. + Made it easier to translate by moving text out of Javascript (not visualiser). + Prepared for translated versions of web page (Apache Multiviews). + Added option to select language of output. + Use HTML output from router to get translated instructions. + + +Version 1.3 of Routino released : Thu Jan 21 2010 +------------------------------------------------- + +Bug fixes: + Ensure output even if the distance between two adjacent route points is small. + Correct the determination of waypoints for abbreviated output. + Check the command line values for filedumper --dump options. + Made the verbose output consistent between different places. + +OSM tagging + Recognise "designation" tag to determine designated paths. + Recognise "steps" tag to determine the highway type. + Recognise "wheelchair" tag to determine if wheelchairs are allowed on highway. + Recognise "moped" tag to determine if mopeds are allowed on a highway. + Recognise "surface" and "paved" tags to determine if a highway is paved. + Recognise "lanes" tag to determine if a highway has multiple lanes. + Recognise "bridge" tag to determine if a highway is a bridge. + Recognise "tunnel" tag to determine if a highway is a tunnel. + +New Features + Remove "bridleway" and "footway" highway types and use "path" highway instead. + Added "steps" as a new highway type separate from the "path" type. + Added "wheelchair" and "moped" to the list of possible transports. + Added "paved", "multilane", "bridge", "tunnel" to list of highway properties. + +Web Pages + Updated for new features listed above. + Added popup to display instructions for each step in route on mouse-over. + Added buttons next to waypoints for: add / remove / move up / move down. + Highlight user selectable parts of form in yellow on mouse-over. + A few small changes, improved CSS, improved Javascript. + +router + For each waypoint choose closest point on a segment and not just closest node. + Added the ability to set preferences based on highway properties. + Changed the text output formats to include bearing and turn information. + + +Version 1.2 of Routino released : Wed Oct 21 2009 +------------------------------------------------- + +OSM tagging + Recognise tags "vehicle" and "motor_vehicle". + Handle duplicate ways in the input OSM file (e.g. concatenation of 2 files). + +Database + Identical ways are combined to reduce database size (~80% fewer ways stored). + +Routing + Fix weight, height, width, length restriction routing. + Allow up to 99 waypoints to be specified instead of 9. + +Visualiser + Don't display speed limits for tracks and paths unless a value is set. + Draw all super-segments that cross the selected boundary. + +Web Pages + A few small changes, improved CSS, improved Javascript. + Changed marker colour when waypoint not selected. + +planetsplitter + Optional slim mode uses minimal memory at the expense of temporary files. + +router + Less CPU time for routing (~30% less). + +filedumper + Allow dumping individual nodes, segments and ways (for debug). + + +Version 1.1 of Routino released : Sat Jun 13 2009 +------------------------------------------------- + +Inputs + Improve parsing of OSM file (imperial units). + Ignore nodes that are missing from the input OSM file. + +Outputs + Create GPX route files as well as GPX track files. + Read in an optional copyright.txt file and include contents in output. + Make better choices about what to output in the abbreviated text file. + +Routing + Allow generating a route with intermediate waypoints. + Use preferences for highway types instead of yes/no choice. + Choice of closest node to start/finish points ensures transport allowed. + +Visualiser + Added data extraction function for viewing routing database data. + +Web Pages + Include full set of web pages for creating customised online router. + +Documentation + Included NEWS.txt file. + Included documentation for installation of web pages. + + +Version 1.0 of Routino released : Wed Apr 08 2009 +------------------------------------------------- + +First version. diff --git a/doc/OUTPUT.txt b/doc/OUTPUT.txt new file mode 100644 index 0000000..4ebd742 --- /dev/null +++ b/doc/OUTPUT.txt @@ -0,0 +1,250 @@ + Routino : Output + ================ + + + There are three different formats of output from the router, HTML, GPX + (GPS eXchange) XML format and plain text with a total of five possible + output files: + * HTML route instructions for each interesting junction. + * GPX track file containing every node. + * GPX route file with waypoints at interesting junctions. + * Plain text description with the interesting junctions. + * Plain text file with every node. + + The "interesting junctions" referred to above are junctions where the + route changes to a different type of highway, more than two highways of + the same type meet, or where the route meets but does not take a more + major highway. When the route follows a major road this definition + eliminates all junctions with minor roads. + + The output files are written to the current directory and are named + depending on the selection of shortest or quickest route. For the + shortest route the file names are "shortest.html", + "shortest-track.gpx", "shortest-route.gpx", "shortest.txt" and + "shortest-all.txt", for the quickest route the names are + "quickest.html", "quickest-track.gpx", "quickest-route.gpx", + "quickest.txt" and "quickest-all.txt". + + The HTML file and GPX files are written out according to the selected + language using the translations contained in the translations.xml + configuration file. + + +HTML Route Instructions +----------------------- + + The HTML route instructions file contains one line for each of the + interesting junctions in the route and one line for the highway that + connects them. + + An example HTML file output is below (some parts are missing, for + example the style definitions): + + + + + + + +Shortest Route +... + + +

Shortest Route

+ +
51.524677 -0.127896 +
Start:At Waypoint, head South-East + +
Follow:Russell Square for 0.391 km, 0.5 min [0.4 km, 0 minutes] +... +
Total:6.3 km, 5 minutes +
Stop:Waypoint +
+ + + + The coordinates are included in the file but are not visible because of + the style definitions. + + +GPX Track File +-------------- + + The GPX track file contains a track with all of the individual nodes + that the route passes through. + + An example GPX track file output is below: + + + + +Creator : Routino - http://www.routino.org/ + +http://creativecommons.org/licenses/by-sa/2.0/ + + + +Shortest route +Shortest route between 'start' and 'finish' waypoints + + +... + + + + + + + +GPX Route File +-------------- + + The GPX route file contains a route (ordered set of waypoints) with all + of the interesting junctions that the route passes through. + + An example GPX route file output is below: + + + + +Creator : Routino - http://www.routino.org/ + +http://creativecommons.org/licenses/by-sa/2.0/ + + + +Shortest route +Shortest route between 'start' and 'finish' waypoints +START +South-East on 'Russell Square' for 0.391 km, 0.5 min +TRIP001 +South-East on 'Russell Square' for 0.055 km, 0.1 min +... +FINISH +Total Journey 6.3 km, 5 minutes + + + + +Text File +--------- + + The text file format contains one entry for all of the interesting + junctions in the route and is intended to be easy to interpret. + + An example text file output is below: + +# Creator : Routino - http://www.routino.org/ +# Source : Based on OpenStreetMap data from http://www.openstreetmap.org/ +# License : http://creativecommons.org/licenses/by-sa/2.0/ +# +#Latitude Longitude Section Section Total Total Point Turn Bearing Highway +# Distance Duration Distance Duration Type + 51.524677 -0.127896 0.000 km 0.0 min 0.0 km 0 min Waypt +3 + 51.521815 -0.124577 0.391 km 0.5 min 0.4 km 0 min Junct +0 +3 Russell Square +... + 51.478353 -0.103561 0.598 km 0.4 min 6.2 km 5 min Junct +2 -3 Camberwell New Road (A202) + 51.478244 -0.103652 0.013 km 0.0 min 6.3 km 5 min Waypt Vassall Road + + The text file output contains a header (indicated by the lines starting + with '#') and then one line for each junction. Each line contains the + information for the route up to that point and the direction to go + next. For each of the lines the individual fields contain the + following: + + Latitude - Location of the point (degrees) + + Longitude - Location of the point (degrees) + + Section Distance - The distance travelled on the section of the journey + that ends at this point (defined on this line). + + Section Duration - The duration of travel on the section of the journey + that ends at this point (defined on this line). + + Total Distance - The total distance travelled up to this point. + + Total Duration - The total duration of travel up to this point. + + Point Type - The type of point; either a waypoint Waypt or junction + Junct. + + Turn - The direction to turn at this point (missing for the first point + since the journey has not started yet and the last point because it has + finished). This can take one of nine values between -4 and +4 defined + by: 0 = Straight, +2 = Right, -2 = Left and +/-4 = Reverse. + + Bearing - The direction to head at this point (missing for the last point + since the journey has finished). This can take one of nine values + between -4 and +4 defined by: 0 = North, +2 = East, -2 = West and +/-4 + = South. + + Highway - The name (or description) of the highway to follow (missing on + the first line). + + The individual items are separated by tabs but some of the items + contain spaces as well. + + +All Nodes Text File +------------------- + + The all nodes text file format contains one entry for each of the nodes + on the route. + + An example all nodes text file output is below: + +# Creator : Routino - http://www.routino.org/ +# Source : Based on OpenStreetMap data from http://www.openstreetmap.org/ +# License : http://creativecommons.org/licenses/by-sa/2.0/ +# +#Latitude Longitude Node Type Segment Segment Total Total Speed Bearing Highway +# Dist Durat'n Dist Durat'n + 51.524677 -0.127896 7485978* Waypt 0.000 0.00 0.00 0.0 + 51.523830 -0.126993 7485047* Junct 0.113 0.14 0.11 0.1 96 146 Woburn Place +... + 51.478353 -0.103561 7576939* Junct 0.104 0.07 6.25 5.0 96 126 Camberwell New Road (A202) + 51.478244 -0.103652 7581605 Waypt 0.013 0.01 6.26 5.0 64 207 Vassall Road + + The all nodes text file output is similar to the text file output + except that a line is printed for each of the nodes rather than just + the interesting junctions. For each of the lines the individual fields + contain the following: + + Latitude - Location of the point in degrees. + + Longitude - Location of the point in degrees. + + Node - The internal node number and an indicator "*" if the node is a + super-node. + + Type - The type of point; a waypoint Waypt, junction Junct, change of + highway Change or intermediate node Inter. + + Segment Distance - The distance travelled on the segment defined on this + line. + + Segment Duration - The duration of travel on the segment defined on this + line. + + Total Distance - The total distance travelled up to this point. + + Total Duration - The total duration of travel up to this point. + + Speed - The speed of travel on the segment defined on this line (missing + on the first line). + + Bearing - The direction that the segment defined on this line travels in + degrees (missing on the first line). + + Highway - The name (or description) of the highway segment (missing on + the first line). + + +-------- + +Copyright 2008-2010 Andrew M. Bishop. diff --git a/doc/README.txt b/doc/README.txt new file mode 100644 index 0000000..75fe606 --- /dev/null +++ b/doc/README.txt @@ -0,0 +1,147 @@ + Routino : OpenStreetMap Routing Software + ======================================== + + + Routino is an application for finding a route between two points using + the dataset of topographical information collected by + http://www.OpenStreetMap.org. + + Starting from the raw OpenStreetMap data (in the form of the '.osm' XML + files available on the internet) a custom database is generated that + contains the information useful for routing. With this database and two + points specified by latitude and longitude an optimum route (either + shortest or quickest) is determined. The route is calculated for + OpenStreetMap highways (roads, paths etc) using one of the common forms + of transport defined in OpenStreetMap (foot, bicycle, horse, motorcar, + motorbike etc). + + When processing the OpenStreetMap data the types of highways are + recorded and these set default limits on the types of traffic allowed. + More specific information about permissions for different types of + transport are also recorded as are maximum speed limits. Further + restrictions like oneway streets, weight, height, width and length + limits are also included where specified. Additionally a set of + properties of each highway are also recorded. The processing of the + input file is controlled by a configuration file which determines the + information that is used. + + When calculating a route the type of transport to be used is taken into + account to ensure that the known restrictions are followed. Each of the + different highway types can further be allowed or disallowed depending + on preferences. For each type of highway a default speed limit is + defined (although the actual speed used will be the lowest of the + default and any specified in the original data). To make use of the + information about restrictions the weight, height, width and length of + the transport can also be specified. Further preferences about road + properties (e.g. paved or not) can also be selected. + + The result of calculating the route can be presented in several + different ways. An HTML file can be produced that contains a + description of the route to take with instructions for each of the + important junctions. The contents of the file are created based on a + set of translations specified in a configuration file. The route is + also available in a GPX (GPS eXchange) XML format. format file + containing either every point and highway segment (a track file) or + just a waypoint and translated instructions for the important junctions + (a route file). Additionally there are two plain text files that + contain all data points or just the important ones (intended for + debugging and further processing). + + One of the design aims of Routino was to make the software are flexible + as possible in selecting routing preferences but also have a sensible + set of default values. Another design aim was that finding the optimum + route should be very fast and most of the speed increases come from the + carefully chosen and optimised data format. + + +Disclaimer +---------- + + The route that is calculated by this software is only as good as the + input data. + + Routino comes with ABSOLUTELY NO WARRANTY for the software itself or + the route that is calculated by it. + + +Demonstration +------------- + + A live demonstration of the router for the UK is available on the + internet: + + http://www.routino.org/uk/router.html + + The source code download available also includes a set of files that can + be used to create your own interactive map. + + The interactive map is made possible by use of the OpenLayers Javascript + library from http://www.openlayers.org/. + + +Documentation +------------- + + The algorithm used is described in the file ALGORITHM.txt and some + notes about the limitations of the data is in DATA.txt. + + The configuration files and in particular the default set of rules for + processing the OpenStreetMap data tags are described in detail in + CONFIGURATION.txt and TAGGING.txt. The format of the output files + generated are described in OUTPUT.txt. + + Detailed information about how to use the programs is available in the + file USAGE.txt and how to install it is in INSTALL.txt. + + +Status +------ + + Version 1.0 of Routino was released on 8th April 2009. + Version 1.1 of Routino was released on 13th June 2009. + Version 1.2 of Routino was released on 21st October 2009. + Version 1.3 of Routino was released on 21st January 2010. + Version 1.4 of Routino was released on 31st May 2010. + + +License +------- + + This program is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + It is important to note that for this program I have decided to use the + Affero GPLv3 instead of just using the GPL. This license adds + additional requirements to anybody who provides a networked service + using this software. + + +Copyright +--------- + + Routino is copyright Andrew M. Bishop 2008-2010. + + Contact amb@gedanken.demon.co.uk for any questions or queries. + + +Homepage +-------- + + The latest information about the program can be found on the homepage: + + http://www.routino.org/ + + +Download +-------- + + The program can be downloaded from: + + http://www.routino.org/download/ + + +-------- + +Copyright 2008-2010 Andrew M. Bishop. diff --git a/doc/TAGGING.txt b/doc/TAGGING.txt new file mode 100644 index 0000000..6c23d8c --- /dev/null +++ b/doc/TAGGING.txt @@ -0,0 +1,352 @@ + Routino : Tagging Rules + ======================= + + + The different tags and attributes in the OSM format XML that are used + by Routino are described below. + + An important change for version 1.4 of Routino is that the tags in the + input file are first processed according to a set of rules defined in a + configuration file. This means that the information presented here is + in two parts; firstly the tags that are recognised by Routino after + pre-processing and secondly the transformations in the default + configuration file. + + +Tags Recognised After Processing +-------------------------------- + + This section describes the tags that are recognised by Routino after + the tag transformations have been applied. This is therefore a much + reduced set of tags compared to the original OSM data and also includes + tags which are specific to Routino. + + In all cases of tag processing values of true, yes, 1 are recognised as + being affirmative and any other value is ignored. + + +Node Tags And Attributes +------------------------ + + None of the node tags are used but the node attributes id, latitude and + longitude of the node. The id atribute is required to associate the + node with the ways and the position attributes are required to locate + the node. + + +Way Tags And Attributes +----------------------- + + The tags from the ways in the data are the ones that provide most of + the information for routing. The id attribute is used only so that the + many segments associated with a way can be share a set of tags taken + from the way. + +The highway Tag +- - - - - - - - + + The most important tag that is used from a way is the highway tag. This + defines the type of highway that the way represents. Any way that does + not have a highway tag is discarded. + + There are more highway types defined than are used by the router. The + subset that the router uses are: + * motorway + * trunk + * primary + * secondary + * tertiary + * unclassified + * residential + * service + * track + * cycleway + * path (1) + * steps (2) + + Note 1: This changed in version 1.3 of Routino - the bridleway and + footway types were included within the path highway type. + Note 2: This changed in version 1.3 of Routino - the steps type was + separated from the footway type. + +Transport Specific Tags +- - - - - - - - - - - - + + One tag is recognised for each of the different modes of transport: + foot, horse, bicycle, wheelchair, moped, motorbike, motorcar, goods, + hgv and psv. These indicate whether the specific type of transport is + allowed on the highway or not. + +The name Tag +- - - - - - + + The name tag is used to provide the label for the highway when printing + the results. + +The ref Tag +- - - - - - + + The ref tag is used to provide the label for the highway when printing + the results. + +The junction Tag +- - - - - - - - + + The junction tag is used to check if a highway is (part of) a + roundabout. This tag is used for information to label the highway if no + other name is provided. + +The multilane Tag +- - - - - - - - - + + The multilane tag is used to identify whether a highway has multiple + lanes for traffic and this sets one of the highway properties. There is + not normally a multilane tag but one needs to be added by the tag + processing transformations. Values of true, yes, 1 are recognised. + +The paved Tag +- - - - - - - + + The paved tag is used to identify whether a highway is paved or not, + this is one of the available highway properties. A paved tag may exist + in the original data but normally the surface tag needs to be + transformed into the paved tag. + +The bridge Tag +- - - - - - - + + The bridge tag is used to identify whether a highway is a bridge and + therefore set one of the available properties. + +The tunnel Tag +- - - - - - - + + The tunnel tag is used to identify whether a highway is a tunnel and + therefore set one of the available properties. + +The oneway Tag +- - - - - - - + + The oneway tag is used to specify that traffic is only allowed to + travel in one direction. + +The maxspeed Tag +- - - - - - - - + + The maxspeed tag is used to specify the maximum speed limit on the + highway; this is always measured in km/hr in OpenStreetMap data. If the + tag value contains "mph" then it is assumed to be a value in those + units and converted to km/hr. + +The maxweight Tag +- - - - - - - - - + + The maxweight tag is used to specify the maximum weight of any traffic + on the way. In other words this must be set to the heaviest weight + allowed on the way (for example a bridge) in tonnes. If the tag value + contains "kg" then it is assumed that the value is in these units and + converted to tonnes. + +The maxheight Tag +- - - - - - - - - + + The maxheight tag is used to specify the maximum height of any traffic + on the way. In other words this must be set to the lowest height of + anything above the way (like a bridge) in metres. If the tag value + contains a measurement in feet or feet and inches then attempts are + made to convert this to metres. + +The maxwidth Tag +- - - - - - - - + + The maxwidth tag is used to specify the maximum width of any traffic on + the way. This must be set to the minimum width of the contraints at the + wayside in metres. If the tag value contains a measurement in feet or + feet and inches then attempts are made to convert this to metres. + +The maxlength Tag +- - - - - - - - - + + The maxlength tag is used to specify the maximum length of any traffic + on the way (usually from a traffic sign) in metres. If the tag value + contains a measurement in feet or feet and inches then attempts are + made to convert this to metres. + + +Relation Tags And Attributes +---------------------------- + + Currently no relation tags or attributes are used. + + +Tag Transformations +------------------- + + This section describes the set of tag transformations that are + contained in the default configuration file. The configuration file + tagging rules are applied in sequence and this section of the document + is arranged in the same order. + + +Node Tag Transformations +------------------------ + + No transformations are applicable since no node tags are recognised. + + +Way Tag Transformations +----------------------- + +Highway Defaults +- - - - - - - - + + The first part of the tag transformations is to decide on defaults for + each type of highway. This uses the highway tag in the OSM file and + maps it into one of the highway tags that are recognised by Routino, + defining the default allowed transport types and adding a number of + properties. + + The first part of the transformation is to convert the highway tag into + one that is recognised by Routino. + + Original tag Transformed tag + ------------ --------------- + bridleway path + byway track + footway path + living_street residential + minor unclassified + pedestrian path + road unclassified + services service + unsurfaced track + unpaved track + walkway path + + The type of highway also determines the defaults for the types of + transport allowed on the highway. The default assumptions are as shown + in the table below. + + Highway foot horse wheelchair bicycle moped motorbike motorcar goods hgv psv + ------- ---- ----- ---------- ------- ----- --------- -------- ----- --- --- + motorway no no no no no yes yes yes yes yes + trunk no no no yes yes yes yes yes yes yes + primary yes yes yes yes yes yes yes yes yes yes + secondary yes yes yes yes yes yes yes yes yes yes + tertiary yes yes yes yes yes yes yes yes yes yes + unclassified yes yes yes yes yes yes yes yes yes yes + residential yes yes yes yes yes yes yes yes yes yes + service yes yes yes yes yes yes yes yes yes yes + track yes yes yes yes no no no no no no + cycleway yes no yes yes no no no no no no + path yes yes(1) yes yes(1) no no no no no no + steps yes no yes no no no no no no no + + Note 1: A path allows bicycle or horse access by default only if + actually labelled as a highway of type "bridleway" or certain values of + the designation tag (described below). + + Finally for the highway tag a number of properties are added depending + on the highway type. + + Highway Properties + ------- ---------- + motorway paved, oneway, multilane + trunk paved + primary paved + secondary paved + tertiary paved + unclassified paved + residential paved + service paved + track paved (1) + cycleway paved + path paved (2) + steps + + Note 1: A track is paved only if it is tagged as tracktype=grade1. + Note 2: A path is paved only if it was originally tagged as + highway=walkway or highway=pedestrian. + +Generic Access Permissions +- - - - - - - - - - - - - + + The access tag is used to specify the default access restrictions on + the way. If the tag value is "no" or "private" then all transport types + are denied access (later tag transformation rules may add specific + transport types back again). + +Other Access Permissions +- - - - - - - - - - - - + + A tag named vehicle means any of the bicycle, moped, motorbike, + motorcar, goods, hgv and psv transport types. A tag named motor_vehicle + is transformed to mean any vehicle except a bicycle. + + The designation tag is used as an alternative method of identifying the + legal right of way on a path (in the UK at least). The tag + transformations convert these tags into a set of allowed transport + types as shown below. + + Designation tag Equivalent access permissions + --------------- ----------------------------- + bridleway or foot=yes, wheelchair=yes, horse=yes, + public_bridleway bicycle=yes + restricted_byway foot=yes, wheelchair=yes, horse=yes, + bicycle=yes + byway foot=yes, wheelchair=yes, horse=yes, + bicycle=yes, moped=yes, motorbike=yes, + motorcar=yes + footpath or foot=yes, wheelchair=yes + public_footpath + +Specific Access Permissions +- - - - - - - - - - - - - - + + The final part of the access permissions is to use the specific + transport type tags. + + One tag is recognised for each of the different modes of transport: + foot, horse, bicycle, wheelchair, moped, motorbike, motorcar, goods, + hgv and psv. These indicate whether the specific type of transport is + allowed on the highway or not. + +Highway Properties +- - - - - - - - - + + If there is a surface tag then the highway is assumed to be unpaved + unless the tag value matches one of the following: paved, asphalt or + concrete. + + Support for the obsolete paved tag is also provided and the highway is + paved if this is set to a true value. + + The lanes tag is used to identify whether a highway has multiple lanes + for traffic or not (the number of lanes is not important in this case, + only whether it is more than one) this sets one of the highway + properties. + + The bridge and tunnel tags are copied directly from the input to the + output. + +Highway Restrictions +- - - - - - - - - - + + The oneway, maxspeed, maxweight, maxheight, maxwidth and maxlength are + copied directly from the input to the output without modification. + +Highway Names and References +- - - - - - - - - - - - - - + + The name and ref tags are copied directly from the input to the output. + + +Relation Tag Transformations +---------------------------- + + No transformations are applicable since no relation tags are recognised. + + +-------- + +Copyright 2008-2010 Andrew M. Bishop. diff --git a/doc/USAGE.txt b/doc/USAGE.txt new file mode 100644 index 0000000..48721bd --- /dev/null +++ b/doc/USAGE.txt @@ -0,0 +1,425 @@ + Routino : Usage + =============== + + + There are four programs that make up this software. The first one takes + the planet.osm datafile from OpenStreetMap (or other source of data + using the same formats) and converts it into a local database. The + second program uses the database to determine an optimum route between + two points. The third program allows visualisation of the data and + statistics to be extracted. The fourth program is a test program for + the tag transformations. + + +planetsplitter +-------------- + + This program reads in the OSM format XML file and splits it up to + create the database that is used for routing. + + Usage: planetsplitter [--help] + [--dir=] [--prefix=] + [--slim] [--sort-ram-size=] + [--tmpdir=] + [--parse-only | --process-only] + [--max-iterations=] + [--tagging=] + [ ...] + + --help + Prints out the help information. + + --dir= + Sets the directory name in which to save the results. Defaults + to the current directory. + + --prefix= + Sets the filename prefix for the files that are created. + Defaults to no prefix. + + --slim + Selects a mode of operation that uses less memory and will + therefore work where virtual memory is very limited or + unavailable. Selecting this option will cause raw data to be + held in disk files with only indexes in RAM. Not using this + option will still use disk files but only for sequential access + and the files are memory mapped for random access. + + --sort-ram-size= + Specifies the amount of RAM (in MB) to use for sorting the data. + If not specified then 64 MB will be used if the '--slim' option + is specified or 256 MB otherwise. + + --tmpdir= + Specifies the name of the directory to store the temporary disk + files. If not specified then it defaults to either the value of + the --dir option or the current directory. + + --parse-only + Parse the input files and store them in a temporary file but + don't process the data into a routing database. + + --process-only + Don't read in any files but process the existing temporary file + into the routing database. + + --max-iterations= + The maximum number of iterations to use when generating + super-nodes and super-segments. Defaults to 10 which is normally + enough. + + --tagging= + The name of the XML file containing the tagging rules (defaults + to 'tagging.xml' with '--dirname' and '--prefix' options). + + ... + Specifies the filename(s) to read data from, by default data is + read from the standard input. + + Note: In version 1.4 of Routino the --transport, --not-highway and + --not-property options have been removed. The same functionality can be + achieved by editing the tagging rules file to not output unwwanted + data. + + Example usage: + +./planetsplitter --dir=data --prefix=gb great_britain.osm + + This will generate the output files 'data/gb-nodes.mem', + 'data/gb-segments.mem' and 'data/gb-ways.mem'. + + +router +------ + + This program performs the calculation of the optimum routes using the + database generated by the planetsplitter program. + + Usage: router [--help | --help-profile | --help-profile-xml | + --help-profile-json | --help-profile-perl ] + [--dir=] [--prefix=] + [--profiles=] [--translations=] + [--exact-nodes-only] + [--quiet] + [--output-html] + [--output-gpx-track] [--output-gpx-route] + [--output-text] [--output-text-all] + [--output-none] + [--profile=] + [--transport=] + [--shortest | --quickest] + --lon1= --lat1= + --lon2= --lon2= + [ ... --lon99= --lon99=] + [--highway-= ...] + [--speed-= ...] + [--property-= ...] + [--oneway=(0|1)] + [--weight=] + [--height=] [--width=] [--length=] + + --help + Prints out the help information. + + --help-profile + Prints out the selected transport profile (type, speed limits, + highway preferences etc.) + + --help-profile-xml + Prints out all the loaded profiles as an XML file in the same + format that can be loaded in. + + --help-profile-json + Prints out all the loaded profiles in JavaScript Object Notation + (JSON) format for use in the interactive webpage. + + --help-profile-perl + Prints out all the loaded profiles as a Perl object for use in + the router CGI. + + --dir= + Sets the directory name in which to read the local database. + Defaults to the current directory. + + --prefix= + Sets the filename prefix for the files in the local database. + Defaults to no prefix. + + --profiles= + Sets the filename containing the list of profiles in XML format. + If the file doesn't exist then dirname, prefix and + "profiles.xml" will be combined and used, if that doesn't exist + then the command line must contain all relevant profile + information. + + --translations= + Sets the filename containing the list of translations in XML + format for the output files. If the file doesn't exist then + dirname, prefix and "translations.xml" will be combined and + used, if that doesn't exist then no file will be read and no + language can be selected. + + --exact-nodes-only + When processing the specified latitude and longitude points only + select the nearest node instead of finding the nearest point + within a segment (quicker but less accurate unless the points + are already near nodes). + + --quiet + Don't generate any screen output while running (useful for + running in a script). + + --language= + Select the language specified from the file of translations. If + this option is not given and the file exists then the first + language in the file will be used. If this option is not given + and no file exists the compiled-in default language (English) + will be used. + + --output-html + --output-gpx-track + --output-gpx-route + --output-text + --output-text-all + Generate the selected output file formats (HTML, GPX track file, + GPX route file, plain text route and/or plain text with all + nodes). If no output is specified then all are generated, + specifying any automatically disables those not specified. + + --output-none + Do not generate any output or read in any translations files. + + --profile= + Specifies the name of the profile to use. + + --transport= + Select the type of transport to use, can be set to: + + + foot = Foot + + horse = Horse + + wheelchair = Wheelchair + + bicycle = Bicycle + + moped = Moped (Small motorbike, limited speed) + + motorbike = Motorbike + + motorcar = Motorcar + + goods = Goods (Small lorry, van) + + hgv = HGV (Heavy Goods Vehicle - large lorry) + + psv = PSV (Public Service Vehicle - bus, coach) + + Defaults to 'motorcar', this option also selects the default + profile information if the '--profile' option is not given and a + profile matching the transport name is found. + + --shortest + Find the shortest route between the waypoints. + + --quickest + Find the quickest route between the waypoints. + + --lon1=, --lat1= + --lon2=, --lat2= + ... --lon99=, --lat99= + The location of the waypoints that make up the start, middle and + end points of the route. Up to 99 waypoints can be specified and + the route will pass through each of the specified ones in + sequence. The algorithm will use the closest node or point + within a segment that allows the specified traffic type. + + --highway-= + Selects the percentage preference for using each particular type + of highway. The value of can be selected from: + + + motorway = Motorway + + trunk = Trunk + + primary = Primary + + secondary = Secondary + + tertiary = Tertiary + + unclassified = Unclassified + + residential = Residential + + service = Service + + track = Track + + cycleway = Cycleway + + path = Path + + steps = Steps + + Default value depends on the profile selected by the --transport + option. + + --speed-= + Selects the speed limit in km/hour for each type of highway. + Default value depends on the profile selected by the --transport + option. + + --property-= + Selects the percentage preference for using each particular + highway property The value of can be selected from: + + + paved = Paved (suitable for normal wheels) + + multilane = Multiple lanes + + bridge = Bridge + + tunnel = Tunnel + + Default value depends on the profile selected by the --transport + option. + + --oneway=[0|1] + Selects if the direction of oneway streets are to be obeyed + (useful to not obey them when walking). Default value depends on + the profile selected by the --transport option. + + --weight= + Specifies the weight of the mode of transport in tonnes; ensures + that the weight limit on the highway is not exceeded. Default + value depends on the profile selected by the --transport option. + + --height= + Specifies the height of the mode of transport in metres; ensures + that the height limit on the highway is not exceeded. Default + value depends on the profile selected by the --transport option. + + --width= + Specifies the width of the mode of transport in metres; ensures + that the width limit on the highway is not exceeded. Default + value depends on the profile selected by the --transport option. + + --length= + Specifies the length of the mode of transport in metres; ensures + that the length limit on the highway is not exceeded. Default + value depends on the profile selected by the --transport option. + + The meaning of the parameter in the command line options + is slightly different for the highway preferences and the property + preferences. For the highway preference consider the choice between two + possible highways between the start and finish when looking for the + shortest route. If highway A has a preference of 100% and highway B has + a preference of 90% then highway A will be chosen even if it is up to + 11% longer (100/90 = 111%). For the highway properties each highway + either has a particular property or not. If the preference for highways + with the property is 60% then the preference for highways without the + property is 40%. The overall preference for the highway is the product + of the highway preference and the preference for highways with (or + without) each property that the highway has (or doesn't have). + + Example usage (motorbike journey, scenic route, not very fast): + + ./router --dir=data --prefix=gb --transport=motorbike --highway-motorway=0 \ + --highway-trunk=0 --speed-primary=80 --speed-secondary=80 --quickest + + This will use the files 'data/gb-nodes.mem', 'data/gb-segments.mem' and + 'data/gb-ways.mem' to find the quickest route by motorbike not using + motorways or trunk roads and not exceeding 80 km/hr. + + +filedumper +---------- + + This program is used to extract statistics from the database, extract + particular information for visualisation purposes or for dumping the + database contents. + + Usage: filedumper [--help] + [--dir=] [--prefix=] + [--statistics] + [--visualiser --latmin= --latmax= + --lonmin= --lonmax= + --data=] + [--dump [--node= ...] + [--segment= ...] + [--way= ...]] + [--dump-osm [--no-super] + [--latmin= --latmax= + --lonmin= --lonmax=]] + + --help + Prints out the help information. + + --dir= + Sets the directory name in which to read the local database. + Defaults to the current directory. + + --prefix= + Sets the filename prefix for the files in the local database. + + --statistics + Prints out statistics about the database files. + + --visualiser + Selects a data visualiser mode which will output a set of data + according to the other parameters below. + + --latmin= --latmax= + The range of latitudes to print the data for. + + --lonmin= --lonmax= + The range of longitudes to print the data for. + + --data= + The type of data to output, can be selected + from: + + o junctions = segment count at each junction. + o super = super-node and super-segments. + o oneway = oneway segments. + o speed = speed limits. + o weight = weight limits. + o height = height limits. + o width = width limits. + o length = length limits. + + --dump + Selects a data dumping mode which allows looking at individual + items in the databases (specifying 'all' instead of a number + dumps all of them). + + --node= + Prints the information about the selected node number + (internal number, not the node id number in the original + source file). + + --segment= + Prints the information about the selected segment number. + + --way= + Prints the information about the selected way number + (internal number, not the way id number in the original + source file). + + --osm-dump + Dumps the contents of the database as an OSM format XML file, + the whole database will be dumped unless the latitude and + longitude ranges are specified. + + --no-super + The super segments will not be output. + + --latmin= --latmax= + The range of latitudes to dump the data for. + + --lonmin= --lonmax= + The range of longitudes to dump the data for. + +tagmodifier +----------- + + This program is used to run the tag transformation process on an OSM + XML file for test purposes. + + Usage: tagmodifier [--help] + [--tagging=] + [] + + --help + Prints out the help information. + + --tagging= + The name of the XML file containing the tagging rules (defaults + to 'tagging.xml' in the current directory). + + ... + Specifies the filename to read data from, by default data is + read from the standard input. + + +-------- + +Copyright 2008-2010 Andrew M. Bishop. diff --git a/doc/html/algorithm.html b/doc/html/algorithm.html new file mode 100644 index 0000000..cec599f --- /dev/null +++ b/doc/html/algorithm.html @@ -0,0 +1,266 @@ + + + + + + +Routino : Algorithm + + + + + + + + +
+ +

Routino : Algorithm

+ +
+
+ + + + + +
+ +

Algorithms

+ +This page describes the development of the algorithm that is used in Routino for +finding routes. + +

Simplest Algorithm

+ +The algorithm to find a route is fundamentally simple: Start at the beginning, +follow all possible routes and keep going until you reach the end. +

+While this method does work, it isn't fast. To be able to find a route quickly +needs a different algorithm, one that can find the correct answer without +wasting time on routes that lead nowhere. + +

Improved Algorithm

+ +The simplest way to do this is to follow all possible segments from the starting +node to the next nearest node (an intermediate node in the complete journey). +For each node that is reached store the shortest route from the starting node +and the length of that route. The list of intermediate nodes needs to be +maintained in order of shortest overall route on the assumption that there is a +straight line route from here to the end node. +
+At each point the intermediate node that has the shortest potential overall +journey time is processed before any other node. From the first node in the +list follow all possible segments and place the newly discovered nodes into the +same list ordered in the same way. This will tend to constrain the list of +nodes examined to be the ones that are between the start and end nodes. If at +any point you reach a node that has already been reached by a longer route then +you can discard that route since the newly discovered route is shorter. +Conversely if the previously discovered route is shorter then discard the new +route. +
+At some point the end node will be reached and then any routes with potential +lengths longer than this actual route can be immediately discarded. The few +remaining potential routes must be continued until they are found to be shorter +or have no possibility of being shorter. The shortest possible route is then +found. +

+At all times when looking at a node only those segments that are possible by the +chosen means of transport are followed. This allows the type of transport to be +handled easily. When finding the quickest route the same rules apply except +that criterion for sorting is the shortest potential route (assuming that from +each node to the end is the fastest possible type of highway). +

+This method also works, but again it isn't very fast. The problem is that the +complexity is proportional to the number of nodes or segments in all routes +examined between the start and end nodes. Maintaining the list of intermediate +nodes in order is the most complex part. + +

Final Algorithm

+ +The final algorithm that is implemented in the router is basically the one above +but with an important difference. Instead of finding a long route among a data +set of 8,000,000 nodes (number of highway nodes in UK at beginning of 2010) it +finds one long route in a data set of 1,000,000 nodes and a few hundred very +short routes in the full data set. Since the time taken to find a route is +proportional to the number of nodes the main route takes 1/10th of the time and +the very short routes take almost no time at all. +

+The solution to making the algorithm fast is therefore to discard most of the +nodes and only keep the interesting ones. In this case a node is deemed to be +interesting if it is the junction of two segments with different properties. In +the algorithm these are classed as super-nodes. Starting at each +super-node a super-segment is generated that finishes on another +super-node and contains the shortest path along segments with identical +properties (and these properties are inherited by the super-segment). The point +of choosing the shortest route is that since all segments considered have +identical properties they will be treated identically when properties are taken +into account. This decision making process can be repeated until the only the +most important and interesting nodes remain. +

+Original data
+Iteration 1
+Iteration 2
+

+To find a route between a start and finish point now comprises the following +steps (assuming a shortest route is required): +

    +
  1. Find all shortest routes from the start point along normal segments and + stopping when super-nodes are reached. +
  2. Find all shortest routes from the end point backwards along normal + segments and stopping when super-nodes are reached. +
  3. Find the shortest route along super-segments from the set of super-nodes + in step 1 to the set of super-nodes in step 2 (taking into account the lengths + found in steps 1 and 2 between the start/finish super-nodes and the ultimate + start/finish point). +
  4. For each super-segment in step 3 find the shortest route between the two + end-point super-nodes. +
+This multi-step process is considerably quicker than using all nodes but gives a +result that still contains the full list of nodes that are visited. There are +some special cases though, for example very short routes that do not pass +through any super-nodes, or routes that start or finish on a super-node. In +these cases one or more of the steps listed can be removed or simplified. + +

Routing Preferences

+ +One of the important features of Routino is the ability to select a route that +is optimum for a set of criteria such as preferences for each type of highway, +speed limits and other restrictions and highway properties. +

+All of these features are handled by assigning a score to each segment while +calculating the route and trying to minimise the score rather than simply +minimising the length. +

+
Segment length +
When calculating the shortest route the length of the segment is the + starting point for the score. +
Speed preference +
When calculating the quickest route the time taken calculated from the + length of the segment and the lower of the highway's own speed limit and the + user's speed preference for the type of highway is the starting point for the + score. +
Oneway restriction +
If a highway has the oneway property in the opposite direction to the + desired travel and the user's preference is to obey oneway restrictions then + the segment is ignored. +
Weight, height, width & length limits +
If a highway has one of these limits and its value is less than the user's + specified requirement then the segment is ignored. +
Highway preference +
The highway preference specified by the user is a percentage, these are + scaled so that the most preferred highway type has a weighted preference of + 1.0 (0% always has a weighted preference of 0.0). The calculated score for a + segment is divided by this weighted preference. +
Highway properties +
The other highway properties are specified by the user as a percentage and + each highway either has that property or not. The user's property preference + is scaled into the range 0.0 (for 0%) to 2.0 (for 100%) to give a weighted + preference, a second "non-property" weighted preference is calcuated in the + same way after subtracting the user's preference from 100%. If a segment has + this property then the calculated score is divided by the weighted preference, + if the segment does not have this property then it is divided by the + non-property weighted preference. +
+ +

Implementation

+ +The hardest part of implementing this router is the data organisation. The +arrangement of the data to minimise the number of operations required to follow +a route from one node to another is much harder than designing the algorithm +itself. +

+The final implementation uses a separate table for nodes, segments and ways. +Each table individually is implemented as a C-language data structure that is +written to disk by a program which parses the OpenStreetMap XML data file. In +the router these data structures are memory mapped so that the operating system +handles the problems of loading the needed data blocks from disk. +

+Each node contains a latitude and longitude and they are sorted geographically +so that converting a latitude and longitude coordinate to a node is fast as well +as looking up the coordinate of a node. The node also contains the location in +the array of segments for the first segment that uses that node. +
+Each segment contains the location of the two nodes as well as the way that the +segment came from. The location of the next segment that uses one of the two +nodes is also stored; the next segment for the other node is the following one +in the array. The length of the segment is also pre-computed and stored. +
+Each way has a name, a highway type, a list of allowed types of traffic, a speed +limit, any weight, height, width or length restrictions and the highway +properties. +

+The super-nodes are mixed in with the nodes and the super-segments are mixed in +with the segments. For the nodes they are the same as the normal nodes, so just +a flag is needed to indicate that they are super. The super-segments are in +addition to the normal segments so they increase the database size (by about +10%) and are also marked with a flag. + +

Practicalities

+ +At the time of writing (April 2010) the OpenStreetMap data for Great Britain +(taken from +GeoFabrik +) contains: +
    +
  • 14,675,098 nodes +
      +
    • 8,767,521 are highway nodes +
    • 1,120,297 are super-nodes +
    +
  • 1,876,822 ways +
      +
    • 1,412,898 are highways +
        +
      • 9,316,328 highway segments +
      • 1,641,009 are super-segments +
      +
    +
  • 60,572 relations +
+ +The database files when generated are 41.5 MB for nodes, 121.6 MB for segments +and 12.6 MB for ways and are stored uncompressed. By having at least 200 MB or +RAM available the routing can be performed with no disk accesses (once the data +has been read once). + + +
+ + + + + + + + + + + + diff --git a/doc/html/configuration.html b/doc/html/configuration.html new file mode 100644 index 0000000..98fa009 --- /dev/null +++ b/doc/html/configuration.html @@ -0,0 +1,256 @@ + + + + + + +Routino : Configuration + + + + + + + + +
+ +

Routino : Configuration

+ +
+
+ + + + + +
+ +

XML Configuration Files

+ +New in version 1.4 of Routino are the use of configuration files to allow more +information to be provided to the programs at run-time. The configuration files +that are used are: +
    +
  • Tagging transformation rules for the planetsplitter program. +
  • Routing profiles for the router program. +
  • Output translations for the router program. +
+ +In keeping with the nature of the input and output files the configuration files +are also XML files. Each of the files uses a custom defined XML schema and an +XSD file is provided for each of them. + +

Tag Transformation Rules

+ +The default name of the tagging transformation rules XML configuration file +is tagging.xml in the same directory as the generated database files. +Other filenames can be specified on the command line using +the --tagging option. When processing the input it is possible to have +a different set of tagging rules for each file; for example different rules for +different countries. + +

+ +The tagging rules allow modifying the highway tags in the source file so that +the routing can be performed on a simpler set of tags. This removes the special +case tagging rules from the source code into the configuration file where they +can be easily modified. Part of the provided tagging.xml file showing the rules +for motorway_link and motorway highway types. + +

+<?xml version="1.0" encoding="utf-8"?>
+<routino-tagging>
+
+  <way>
+
+    <if k="highway" v="motorway_link">
+      <set v="motorway"/>
+    </if>
+
+    <if k="highway" v="motorway">
+      <output k="highway"/>
+
+      <output k="motorbike"  v="yes"/>
+      <output k="motorcar"   v="yes"/>
+      <output k="goods"      v="yes"/>
+      <output k="hgv"        v="yes"/>
+      <output k="psv"        v="yes"/>
+
+      <output k="paved"      v="yes"/>
+      <output k="multilane"  v="yes"/>
+      <output k="oneway"     v="yes"/>
+    </if>
+...
+  <way>
+
+</routino-tagging>
+
+ +The rules all have the same format; an if element for matching the +input and some set or output elements to either change the +input tags or create an output tag. The k and v attributes +have the same meaning as the attributes with the same names in the OSM XML file +- the tag key and tag value. + +

+ +An if rule that has both k and v specified is only +applied if a tag exists in the input that matches both. An if rule +that has only the k attribute is applied if a tag with that key exists +and an if rule that has only the v attribute is applied to all +tags with that value. + +

+ +For the set and output elements the tag that is created in the +input or output tag set uses the k and v attributes specified. +If one or both are not specified then the original ones are used. + + +

Routing Profiles

+ +The default name of the routing profiles XML configuration file +is profiles.xml in the same directory as the database files. Other +filenames can be specified on the command line using the --tagging +option. + +

+ +The purpose of this configuration file is to allow easy modification of the +routing parameters so that they do not all need to be specified on the command +line. In versions of Routino before version 1.4 the default routing parameters +(preferred highways, preferred speeds etc) were contained in the source code, +now they are in a configuration file. When calculating a route +the --profile option selects the named profile from the configuration +file. + +

+ +Part of the provided profiles.xml file showing the parameters for transport on +foot is shown below: + +

+<?xml version="1.0" encoding="UTF-8" ?>
+<routino-profiles>
+
+  <profile name="foot" transport="foot">
+    <speeds>
+...
+      <speed highway="cycleway"      kph="4" />
+      <speed highway="path"          kph="4" />
+      <speed highway="steps"         kph="4" />
+    </speeds>
+    <preferences>
+...
+      <preference highway="cycleway"      percent="95" />
+      <preference highway="path"          percent="100" />
+      <preference highway="steps"         percent="80" />
+    </preferences>
+    <properties>
+      <property type="paved"      percent="50" />
+      <property type="multilane"  percent="25" />
+      <property type="bridge"     percent="50" />
+      <property type="tunnel"     percent="50" />
+    </properties>
+    <restrictions>
+      <oneway obey="0" /> 
+      <weight limit="0.0" />
+      <height limit="0.0" />
+      <width  limit="0.0" />
+      <length limit="0.0" />
+    </restrictions>
+  </profile>
+  <profile name="horse" transport="horse">
+...
+  </profile>
+...
+</routino-profiles>
+
+ + +

Output Translations

+ +The default name of the output translations XML configuration file +is translations.xml in the same directory as the database files. Other +filenames can be specified on the command line using the --translations +option. + +

+ +The generated HTML and GPX output files (described in the next section) are +created using the fragments of text that are defined in this file. Additional +languages can be added to the file and are selected using +the --language option to the router. If no language is specified the +first one in the file is used. + +

+ +Part of the provided translations.xml file showing some of the English language +(en) translations is shown below: + +

+<?xml version="1.0" encoding="utf-8"?>
+<routino-translations>
+
+  <language lang="en">
+...
+    <turn direction="-4" string="Very sharp left" />
+    <turn direction="-3" string="Sharp left" />
+    <turn direction="-2" string="Left" />
+...
+    <heading direction="-4" string="South" />
+    <heading direction="-3" string="South-West" />
+    <heading direction="-2" string="West" />
+...
+    <route type="shortest" string="Shortest" />
+    <route type="quickest" string="Quickest" />
+    <output-html>
+...
+    </output-html>
+    <output-gpx>
+...
+    </output-gpx>
+  </language>
+</routino-translations>
+
+ +
+ + + + + + + + + + + + diff --git a/doc/html/data.html b/doc/html/data.html new file mode 100644 index 0000000..52f74aa --- /dev/null +++ b/doc/html/data.html @@ -0,0 +1,165 @@ + + + + + + +Routino : Data + + + + + + + + +
+ +

Routino : Data

+ +
+
+ + + + + +
+ +

Data

+ +A router relies on data to be able to find a route. + +

OpenStreetMap Data

+ +The data that is collected by the OpenStreetMap project consists of +nodes, ways and relations. +
+
Node +
A node is a point that has a latitude and longitude and attributes that + describe what type of point it is (part of a way or a place of interest for + example). +
Way +
A way is a collection of nodes that when joined together define something + (for example a road, a ralway, a boundary, a building, a lake etc). The + ways also have attributes that define them (speed limits, type of road and + restrictions for example). +
Relation +
A relation is a collection of items (usually ways) that are related to + each other for some reason (highways that make up a route for example). +
+ +The +OpenStreetMap Wiki +explains the data much better than I can. + +

Router Data

+ +The information that is needed by a routing algorithm is only a subset of the +information that is collected by the OpenStreetMap project. For routing what is +required is information about the location of roads (or other highways), the +connections between the highways and the properties of those highways. +
+
Location of highways (nodes) +
The locations of things is provided by the nodes from the OpenStreetMap + data. The nodes are the only things that have coordinates in + OpenStreetMap and everything else is made up by reference to them. Not + all of the nodes are useful, only the ones that are part of highways. The + location of the nodes is stored but none of the other attributes are + currently used by the router. +
Location of highways (ways) +
The location of the highways is defined in the OpenStreetMap data by the + ways. Only the highway ways are useful and the other ways are discarded. + What remains is lists of nodes that join together to form a section of + highway. This is further split into segments which are + individual parts of a way connected by two nodes. +
Properties of highways (tags) +
The ways that belong to highways are extracted from the data in the + previous step and for each way the useful information for routing is + stored. For the router the useful information is the type of highway, the + speed limit, the allowed types of transport and other restrictions + (one-way, min height, max weight etc). +
Connections between highways +
The connections between highways are defined in the OpenStreetMap data by + ways that share nodes. Since the ways may join in the middle and not just + the ends it is the segments defined above that are not part of the + OpenStreetMap data that are most important. +
+ +The information that is extracted from the OpenStreetMap data is stored in an +optimised way that allows the routing to be performed quickly. + +

Interpreting Data Tags

+ +The tags are the information that is attached to the nodes and ways in +OpenStreetMap. The router needs to interpret these tags and use them when +deciding what type of traffic can use a highway (for example). +

+ +There are no well defined rules in OpenStreetMap about tagging, but there is +guidance on the +OpenStreetMap Wiki "Map_Features" +page. This describes a set of recommended tags but these are not universally used +so it is up to each application how to interpret them. +

+ +The tagging rules that the router +uses are very important in controlling how the router works. With Routino the +data tags can be modified when the data is imported to allow customisation of +the information used for routing. + + +

Problems With OpenStreetMap Data

+ +The route that can be found is only as good as the data that is available. This +is not intended as a criticism of the OpenStreetMap data; it is generally good. +

+There are some problems that are well known and which affect the router. For +example highways might be missing because nobody has mapped them. A highway may +be wrongly tagged with incorrect properties, or a highway might be missing +important tags for routing (e.g. speed limits). There can also be problems +with highways that should join but don't because they do not share nodes. +

+A lot of these problems can be found using the interactive data visualiser that +uses the same Routino rouing database. + + +

+ + + + + + + + + + + + diff --git a/doc/html/example0.png b/doc/html/example0.png new file mode 100644 index 0000000000000000000000000000000000000000..4401486c02a9f39ae5434303ab61593045d3cd3e GIT binary patch literal 11045 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 t=zu^P=7xr?!U+jZ3|}qqK?^AtB + + + + + +Routino : Documentation + + + + + + + + +
+ +

Routino : Documentation

+ +
+
+ + + + + +
+ + +

Data

+ +A good router relies on good data and the +OpenStreetMap +data is a good source. There are however a number of things that need to be +considered about +the data used. + + +

Tagging

+ +In addition to the raw data the way that are tags are used is also important. +With Routino the +tagging rules +are contained in a configuration file and can easily be customised to change the +interpretation of each tag. + + +

Program Usage

+ +There are four programs that make up this software, two create the routing +database and use the information in it and the other two perform additional functions. +Full instructions +for using the four programs are provided. + + +

Configuration Files

+ +When the programs are run they read in one or more +configuration files. +These files contain information about the routing preferences (types of highways, +prefered speeds etc), tagging rules and translation information for the outputs. + + +

Output Files

+ +The final result of running the router is one or more +output files +that contain the calculated route. + + +

Algorithm

+ +The algorithm that is used by +Routino takes the OpenStreetMap data and creates a local database of the +important information for rapid routing. + + +

Installation

+ +The Routino source code comes with a set of files that can be used to create +a working server very easily. The full information about +installation +describes how to compile the programs and install them. + + +
+ + + + + + + + + + + + diff --git a/doc/html/installation.html b/doc/html/installation.html new file mode 100644 index 0000000..faa752f --- /dev/null +++ b/doc/html/installation.html @@ -0,0 +1,243 @@ + + + + + + +Routino : Installation + + + + + + + + +
+ +

Routino : Installation

+ +
+
+ + + + + +
+ + +

Compilation

+ +This program has been written to run on Linux, no cross-platform compatibility +has been specifically included but on the other hand nothing platform specific +has been knowingly included either. + +

+ +Any information on improving the compilation process on anything other than +32-bit x86 Linux is welcome. + +

+ +No external libraries are required and the programs are written in standard C +language. + +

+ +To compile the programs just type 'make'. + + +

Installation

+ +After compilation the executable files are copied into the directory +web/bin and the default XML configuration files are copied into the +directory web/data. This is in preparation for using the supplied +example web pages but is also a useful location to copy the files from for +normal use. + +

+ +The executable files are called planetsplitter, router and +filedumper (also tagmodifier for debugging tag modifications). +They can be copied to any location and need no special installation environment. + +

+ +The default configuration files are called profiles.xml, +tagging.xml and translations.xml. The names of the +configuration files can be specified on the command line but by default are also +looked for in the directory that contains the routing database. + + +

Example Web Page

+ +The directory web contains a set of files that can be used to create a +working set of web pages with interfaces to the routing algorithm. + +

+ +The files in the web directory will require copying to a location that +is accessible by a web server. After copying the files some of them need to be +edited; search through the files for lines that contain the words "EDIT THIS" +and make appropriate edits. The files that need editing are paths.pl +(to set the directory paths) and router.js and visualiser.js +to limit the range of the visible map (latitude, longitude and zoom). + + +

Configuration of web files

+ +The assumption in this description is that the whole of the directory called +web is copied into a directory that is accessible by an Apache web +server. + +

+ +This is not a secure configuration but an easy one to configure. +
+Only the directory www should be accessible by the web server. +
+Do not use this configuration unmodified in a public web server. + +

+ +The directory structure is as follows: + +

+   web/
+    |
+    + /bin/                    <- The Routino executable files (when compiled).
+    |
+    + /data/                   <- The Routino database and default configuration
+    |                             files.
+    |
+    + /results/                <- An empty directory to store the results.
+    |
+    + /www/                    <- The files that must be available to the web
+        |                         server are below this level.
+        |
+        + /openlayers/         <- A directory to hold the OpenLayers scripts.
+        |
+        + /routino/            <- The main HTML, Javascript, CSS and CGI files.
+            |
+            + /documentation/  <- The HTML version of the Routino documentation.
+
+ +The directory bin will be filled by running the compilation process. +For a secure installation the bin directory should be outside of the +web server, the file www/routino/paths.pl contains the path to +the bin directory. + +

+ +The directory data must contain the Routino database and is also the +default location for the configuration files. The routing database is created +by downloading the OSM files for the region of interest and running the +planetsplitter program. There is a script in the directory that will download +the OSM files and create the required database. The script should be edited to +set the names of the files to be downloaded. For a secure installation +the data directory should be outside of the web server, the +file www/routino/paths.pl contains the path to the data +directory. + +

+ +The directory results is a temporary directory that it used to hold the +GPX and text files generated by the Routino router. The directory must be +writable by the web server process since it is the CGI scripts that are run by +the web server that writes the results here. For a secure installation +the results directory should be outside of the web server, the file +www/routino/paths.pl contains the path to the results directory. + +

+ +The directory www and its sub-directories are the only ones that need +to be within the web server accessible directory. + +

+ +The directory www/openlayers must be filled with the openlayers +Javascript library that can be downloaded from http://www.openlayers.org/. +(This version of Routino has been tested with OpenLayers library version 2.9.1). +The files must be installed so that the file www/openlayers/OpenLayers.js +and the directories www/openlayers/img/, www/openlayers/theme/ +all exist. There is a script in the directory that will automatically download +the files, create an optimised "OpenLayers.js" and copy the files to the required +locations. + +

+ +The directory www/routino contains the main HTML, Javascript and CSS +files as well as the CGI scripts that perform the server-side routing functions. +The description below lists all of the files that contain editable information. + +

+
paths.pl +
This contains the names of the directories that contain the executable + files, router database and temporary results. +
router.pl +
This file contains the filename prefix for the routing database files + (only needed if planetsplitter is run with the --prefix option). +
router.js +
The parameters in this file control the boundary of the visible map + (defaults to UK), the minimum and maximum zoom levels (defaults to between + 4 and 15 inclusive) and the source of map tiles (defaults to the main + OpenStreetMap tile server). +
visualiser.js +
The same parameters as in router.js are in this file. +
+ +

+ +The directory www/routino/documentation contains the HTML version of +the Routino documentation. + + +

Configuration of web server

+ +The file www/routino/.htaccess contains all of the Apache configuration +options that are required to get the example web pages running. The only +problem is that because of the way that the AllowOverride option works +one of the configuration options has been commented out. This must be enabled +in the main Apache server configuration file. + + +
+ + + + + + + + + + + + diff --git a/doc/html/output.html b/doc/html/output.html new file mode 100644 index 0000000..566d4ac --- /dev/null +++ b/doc/html/output.html @@ -0,0 +1,364 @@ + + + + + + +Routino : Output + + + + + + + + +
+ +

Routino : Output

+ +
+
+ + + + + +
+ +

Router Output

+ +There are three different formats of output from the router, HTML, +GPX (GPS eXchange) XML format +and plain text with a total of five possible output files: +
    +
  • HTML route instructions for each interesting junction. +
  • GPX track file containing every node. +
  • GPX route file with waypoints at interesting junctions. +
  • Plain text description with the interesting junctions. +
  • Plain text file with every node. +
+ +The "interesting junctions" referred to above are junctions where the route +changes to a different type of highway, more than two highways of the same type +meet, or where the route meets but does not take a more major highway. When the +route follows a major road this definition eliminates all junctions with minor +roads. + +

+ +The output files are written to the current directory and are named depending on +the selection of shortest or quickest route. For the shortest route the file +names are "shortest.html", "shortest-track.gpx", "shortest-route.gpx", +"shortest.txt" and "shortest-all.txt", for the quickest route the names are +"quickest.html", "quickest-track.gpx", "quickest-route.gpx", "quickest.txt" and +"quickest-all.txt". + +

+ +The HTML file and GPX files are written out according to the selected language +using the translations contained in the translations.xml configuration file. + + + +

HTML Route Instructions

+ +The HTML route instructions file contains one line for each of the interesting +junctions in the route and one line for the highway that connects them. + +

+ +An example HTML file output is below (some parts are missing, for example the +style definitions): + +

+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<HTML>
+<!-- Creator : Routino - http://www.routino.org/ -->
+<!-- Source : Based on OpenStreetMap data from http://www.openstreetmap.org/ -->
+<!-- License : http://creativecommons.org/licenses/by-sa/2.0/ -->
+<HEAD>
+<TITLE>Shortest Route</TITLE>
+...
+</HEAD>
+<BODY>
+<H1>Shortest Route</H1>
+<table>
+<tr class='c'><td class='l'><td class='r'>51.524677 -0.127896
+<tr class='n'><td class='l'>Start:<td class='r'>At <span class='w'>Waypoint</span>, head <span class='b'>South-East</span>
+
+<tr class='s'><td class='l'>Follow:<td class='r'><span class='h'>Russell Square</span> for <span class='d'>0.391 km, 0.5 min</span> [<span class='j'>0.4 km, 0 minutes</span>]
+...
+<tr class='t'><td class='l'>Total:<td class='r'><span class='j'>6.3 km, 5 minutes</span>
+<tr><td class='l'>Stop:<td class='r'><span class='w'>Waypoint</span>
+</table>
+</BODY>
+</HTML>
+
+ +The coordinates are included in the file but are not visible because of the +style definitions. + +

GPX Track File

+ +The GPX track file contains a track with all of the individual nodes that the +route passes through. + +

+ +An example GPX track file output is below: + +

+<?xml version="1.0" encoding="UTF-8"?>
+<gpx version="1.1" creator="Routino" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.topografix.com/GPX/1/1" xsi:schemaLocation="http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd">
+<metadata>
+<desc>Creator : Routino - http://www.routino.org/</desc>
+<copyright author="Based on OpenStreetMap data from http://www.openstreetmap.org/">
+<license>http://creativecommons.org/licenses/by-sa/2.0/</license>
+</copyright>
+</metadata>
+<trk>
+<name>Shortest route</name>
+<desc>Shortest route between 'start' and 'finish' waypoints</desc>
+<trkpt lat="51.524677" lon="-0.127896"/>
+<trkpt lat="51.523830" lon="-0.126993"/>
+...
+<trkpt lat="51.478353" lon="-0.103561"/>
+<trkpt lat="51.478244" lon="-0.103652"/>
+</trkseg>
+</trk>
+</gpx>
+
+ + +

GPX Route File

+ +The GPX route file contains a route (ordered set of waypoints) with all of the +interesting junctions that the route passes through. + +

+ +An example GPX route file output is below: + +

+<?xml version="1.0" encoding="UTF-8"?>
+<gpx version="1.1" creator="Routino" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.topografix.com/GPX/1/1" xsi:schemaLocation="http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd">
+<metadata>
+<desc>Creator : Routino - http://www.routino.org/</desc>
+<copyright author="Based on OpenStreetMap data from http://www.openstreetmap.org/">
+<license>http://creativecommons.org/licenses/by-sa/2.0/</license>
+</copyright>
+</metadata>
+<rte>
+<name>Shortest route</name>
+<desc>Shortest route between 'start' and 'finish' waypoints</desc>
+<rtept lat="51.524677" lon="-0.127896"><name>START</name>
+<desc>South-East on 'Russell Square' for 0.391 km, 0.5 min</desc></rtept>
+<rtept lat="51.521815" lon="-0.124577"><name>TRIP001</name>
+<desc>South-East on 'Russell Square' for 0.055 km, 0.1 min</desc></rtept>
+...
+<rtept lat="51.478244" lon="-0.103652"><name>FINISH</name>
+<desc>Total Journey 6.3 km, 5 minutes</desc></rtept>
+</rte>
+</gpx>
+
+ + +

Text File

+ +The text file format contains one entry for all of the interesting junctions in +the route and is intended to be easy to interpret. + +

+ +An example text file output is below: + +

+# Creator : Routino - http://www.routino.org/
+# Source : Based on OpenStreetMap data from http://www.openstreetmap.org/
+# License : http://creativecommons.org/licenses/by-sa/2.0/
+#
+#Latitude	Longitude	Section 	Section 	Total   	Total   	Point	Turn	Bearing	Highway
+#        	         	Distance	Duration	Distance	Duration	Type 	    	       	       
+ 51.524677	  -0.127896	 0.000 km	 0.0 min	  0.0 km	   0 min	Waypt		 +3	
+ 51.521815	  -0.124577	 0.391 km	 0.5 min	  0.4 km	   0 min	Junct	 +0	 +3	Russell Square
+...
+ 51.478353	  -0.103561	 0.598 km	 0.4 min	  6.2 km	   5 min	Junct	 +2	 -3	Camberwell New Road (A202)
+ 51.478244	  -0.103652	 0.013 km	 0.0 min	  6.3 km	   5 min	Waypt			Vassall Road
+
+ +

+ +The text file output contains a header (indicated by the lines starting with +'#') and then one line for each junction. Each line contains the information +for the route up to that point and the direction to go next. + +For each of the lines the individual fields contain the following: + + + + + + + + + + + + + +
Item + Description +
Latitude + Location of the point (degrees) +
Longitude + Location of the point (degrees) +
Section Distance + The distance travelled on the section of the journey that ends at this + point (defined on this line). +
Section Duration + The duration of travel on the section of the journey that ends at this + point (defined on this line). +
Total Distance + The total distance travelled up to this point. +
Total Duration + The total duration of travel up to this point. +
Point Type + The type of point; either a waypoint Waypt or + junction Junct. +
Turn + The direction to turn at this point (missing for the first point since + the journey has not started yet and the last point because it has + finished). This can take one of nine values between -4 and +4 defined by: + 0 = Straight, +2 = Right, -2 = Left and +/-4 + = Reverse. +
Bearing + The direction to head at this point (missing for the last point since + the journey has finished). This can take one of nine values between -4 + and +4 defined by: 0 = North, +2 = East, -2 + = West and +/-4 = South. +
Highway + The name (or description) of the highway to follow (missing on the first + line). +
+ +

+ +The individual items are separated by tabs but some of the items contain spaces +as well. + + +

All Nodes Text File

+ +The all nodes text file format contains one entry for each of the nodes on the +route. + +

+ +An example all nodes text file output is below: + +

+# Creator : Routino - http://www.routino.org/
+# Source : Based on OpenStreetMap data from http://www.openstreetmap.org/
+# License : http://creativecommons.org/licenses/by-sa/2.0/
+#
+#Latitude	Longitude	    Node	Type	Segment	Segment	Total	Total  	Speed	Bearing	Highway
+#        	         	        	    	Dist   	Durat'n	Dist 	Durat'n	     	       	       
+ 51.524677	  -0.127896	 7485978*	Waypt	0.000	 0.00	 0.00	  0.0			
+ 51.523830	  -0.126993	 7485047*	Junct	0.113	 0.14	 0.11	  0.1	 96	 146	Woburn Place
+...
+ 51.478353	  -0.103561	 7576939*	Junct	0.104	 0.07	 6.25	  5.0	 96	 126	Camberwell New Road (A202)
+ 51.478244	  -0.103652	 7581605 	Waypt	0.013	 0.01	 6.26	  5.0	 64	 207	Vassall Road
+
+ +

+ +The all nodes text file output is similar to the text file output except that a +line is printed for each of the nodes rather than just the interesting junctions. + +For each of the lines the individual fields contain the following: + + + + + + + + + + + + + + +
Item + Description +
Latitude + Location of the point in degrees. +
Longitude + Location of the point in degrees. +
Node + The internal node number and an indicator "*" if the node is a super-node. +
Type + The type of point; a waypoint Waypt, junction Junct, + change of highway Change or intermediate node Inter. +
Segment Distance + The distance travelled on the segment defined on this line. +
Segment Duration + The duration of travel on the segment defined on this line. +
Total Distance + The total distance travelled up to this point. +
Total Duration + The total duration of travel up to this point. +
Speed + The speed of travel on the segment defined on this line (missing on the + first line). +
Bearing + The direction that the segment defined on this line travels in degrees + (missing on the first line). +
Highway + The name (or description) of the highway segment (missing on the first + line). +
+ + +

+ + + + + + + + + + + + diff --git a/doc/html/style.css b/doc/html/style.css new file mode 100644 index 0000000..535e062 --- /dev/null +++ b/doc/html/style.css @@ -0,0 +1,391 @@ +/* +// Routino web page style sheet. +// +// Part of the Routino routing software. +// +// This file Copyright 2008-2010 Andrew M. Bishop +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . +*/ + + +/*----------------------------------*/ +/* Body HTML formatting */ +/*----------------------------------*/ + +BODY +{ + /* fonts and text styles */ + + font-family: sans-serif; + font-size: medium; + + /* margins, borders, padding and sizes */ + + padding: 0; + + margin: 0; +} + +A.ext +{ + /* fonts and text styles */ + + text-decoration: underline; +} + +PRE +{ + /* fonts and text styles */ + + font-family: monospace; +} + +PRE.boxed +{ + /* margins, borders, padding and sizes */ + + padding: 0.5em; + + border: solid; + border-width: thin; +} + + +/*-----------------------------------*/ +/* Header HTML formatting */ +/*-----------------------------------*/ + +DIV.header +{ + /* margins, borders, padding and sizes */ + + padding: 0; + padding-top: 0.5em; + padding-bottom: 0.5em; + + border-width: 0; + border-bottom: solid; + border-bottom-width: thin; + + margin: 0; + + /* floats */ + + clear: left; +} + +DIV.header HR /* Horizontal rule, only visible without CSS */ +{ + display: none; +} + +DIV.header H1 +{ + /* fonts and text styles */ + + font-size: xx-large; + + font-weight: bold; + + text-decoration: underline; + + /* margins, borders, padding and sizes */ + + padding: 0.25em; + + border: 0; + + margin: 0; +} + + +/*-----------------------------------*/ +/* Footer HTML formatting */ +/*-----------------------------------*/ + +DIV.footer +{ + /* fonts and text styles */ + + font-size: small; + + /* margins, borders, padding and sizes */ + + padding: 0; + padding-top: 0.5em; + padding-bottom: 0.5em; + + border-width: 0; + border-top: solid; + border-top-width: thin; + + margin: 0; + + /* floats */ + + clear: left; +} + +DIV.footer HR /* Horizontal rule, only visible without CSS */ +{ + display: none; +} + + +/*-----------------------------------*/ +/* Content HTML formatting */ +/*-----------------------------------*/ + +DIV.content +{ + /* margins, borders, padding and sizes */ + + padding: 0.5em; + + border-width: 0; +} + +DIV.content H1 +{ + /* fonts and text styles */ + + font-size: xx-large; + font-weight: bold; + + /* margins, borders, padding and sizes */ + + padding: 0; + + margin-top: 1em; + margin-bottom: 0.25em; +} + +DIV.content H2 +{ + /* fonts and text styles */ + + font-size: x-large; + font-weight: bold; + + /* margins, borders, padding and sizes */ + + padding: 0; + + margin-top: 0.75em; + margin-bottom: 0.25em; +} + +DIV.content H3 +{ + /* fonts and text styles */ + + font-size: large; + font-weight: bold; + + /* margins, borders, padding and sizes */ + + padding: 0; + + margin-top: 0.75em; + margin-bottom: 0.25em; +} + +DIV.content H4 +{ + /* fonts and text styles */ + + font-size: medium; + font-weight: bold; + + /* margins, borders, padding and sizes */ + + padding: 0; + + margin-top: 0.5em; + margin-bottom: 0.125em; +} + +DIV.content OL, DIV.content UL, DIV.content DIR, DIV.content MENU, DIV.content DL +{ + /* margins, borders, padding and sizes */ + + padding-top: 0; + padding-bottom: 0; + + margin-top: 0.25em; + margin-bottom: 0.25em; +} + +DIV.content UL UL, DIV.content UL OL, DIV.content UL DL, DIV.content OL UL, DIV.content OL OL, DIV.content OL DL, DIV.content DL UL, DIV.content DL OL, DIV.content DL DL +{ + /* margins, borders, padding and sizes */ + + padding-top: 0; + padding-bottom: 0; + + margin-top: 0; + margin-bottom: 0; +} + +DIV.content FORM +{ + /* margins, borders, padding and sizes */ + + padding: 0.5em; + + margin: 0.5em; +} + +DIV.content INPUT +{ + /* margins, borders, padding and sizes */ + + padding: 0; + + border: 1px solid; + + margin: 1px; +} + +DIV.content BUTTON +{ + /* margins, borders, padding and sizes */ + + padding: 0; + + border: 1px solid; + + margin: 1px; +} + +DIV.content INPUT.left +{ + /* text alignment */ + + text-align: left; +} + +DIV.content INPUT.center +{ + /* text alignment */ + + text-align: center; +} + +DIV.content INPUT.right +{ + /* text alignment */ + + text-align: right; +} + +DIV.content TABLE +{ + /* margins, borders, padding and sizes */ + + padding: 0; + + border: 2px solid; + + margin: 0; + margin-left: auto; + margin-right: auto; + + border-collapse: collapse; +} + +DIV.content TABLE.noborder +{ + /* margins, borders, padding and sizes */ + + margin-left: auto; + margin-right: auto; + + border: 0; +} + +DIV.content TABLE.noborder-left +{ + /* margins, borders, padding and sizes */ + + margin-left: 0; + margin-right: auto; + + border: 0; +} + +DIV.content CAPTION +{ + /* position */ + + caption-side: bottom; + + /* text alignment */ + + text-align: center; + + /* fonts and text styles */ + + font-weight: bold; +} + +DIV.content TD, DIV.content TH +{ + /* margins, borders, padding and sizes */ + + border: 1px solid; +} + +DIV.content TABLE.noborder TD, DIV.content TABLE.noborder TH +{ + /* margins, borders, padding and sizes */ + + border: 0; +} + +DIV.content TABLE.noborder-left TD, DIV.content TABLE.noborder-left TH +{ + /* margins, borders, padding and sizes */ + + border: 0; +} + +DIV.content TD.left, DIV.content TH.left, DIV.content TR.left +{ + /* text alignment */ + + text-align: left; +} + +DIV.content TD.center, DIV.content TH.center, DIV.content TR.center +{ + /* text alignment */ + + text-align: center; +} + +DIV.content TD.right, DIV.content TH.right, DIV.content TR.right +{ + /* text alignment */ + + text-align: right; +} + +DIV.content IMG +{ + /* margins, borders, padding and sizes */ + + border: 0px; +} diff --git a/doc/html/tagging.html b/doc/html/tagging.html new file mode 100644 index 0000000..5af6812 --- /dev/null +++ b/doc/html/tagging.html @@ -0,0 +1,658 @@ + + + + + + +Routino : Tagging Rules + + + + + + + + +
+ +

Routino : Tagging Rules

+ +
+
+ + + + + +
+ +

Tags And Attributes

+ +The different tags and attributes in the +OSM +format XML that are used by Routino are described below. + +

+ +An important change for version 1.4 of Routino is that the tags in the input +file are first processed according to a set of rules defined in a configuration +file. This means that the information presented here is in two parts; firstly +the tags that are recognised by Routino after pre-processing and secondly the +transformations in the default configuration file. + + +

Tags Recognised After Processing

+ +This section describes the tags that are recognised by Routino after the tag +transformations have been applied. This is therefore a much reduced set of tags +compared to the original OSM data and also includes tags which are specific to +Routino. + +

+ +In all cases of tag processing values of true, yes, 1 +are recognised as being affirmative and any other value is ignored. + + +

Node Tags And Attributes

+ +None of the node tags are used but the node attributes id, latitude +and longitude of the node. The id atribute is required to associate the +node with the ways and the position attributes are required to locate the node. + + +

Way Tags And Attributes

+ +The tags from the ways in the data are the ones that provide most of the +information for routing. The id attribute is used only so that the +many segments associated with a way can be share a set of tags taken from the +way. + + +

The highway Tag

+ +The most important tag that is used from a way is the highway tag. +This defines the type of highway that the way represents. Any way that does not +have a highway tag is discarded. + +

+ +There are more highway types defined than are used by the router. The subset +that the router uses are: + +

    +
  • motorway +
  • trunk +
  • primary +
  • secondary +
  • tertiary +
  • unclassified +
  • residential +
  • service +
  • track +
  • cycleway +
  • path (1) +
  • steps (2) +
+ +

+ + + Note 1: This changed in version 1.3 of Routino - the bridleway and footway + types were included within the path highway type. +
+ Note 2: This changed in version 1.3 of Routino - the steps type was separated + from the footway type. +
+ + +

Transport Specific Tags

+ +One tag is recognised for each of the different modes of transport: foot, +horse, bicycle, wheelchair, moped, +motorbike, motorcar, goods, hgv +and psv. These indicate whether the specific type of transport is +allowed on the highway or not. + + +

The name Tag

+ +The name tag is used to provide the label for the highway when printing +the results. + + +

The ref Tag

+ +The ref tag is used to provide the label for the highway when printing +the results. + + +

The junction Tag

+ +The junction tag is used to check if a highway is (part of) a +roundabout. This tag is used for information to label the highway if no other +name is provided. + + +

The multilane Tag

+ +The multilane tag is used to identify whether a highway has multiple +lanes for traffic and this sets one of the highway properties. There is not +normally a multilane tag but one needs to be added by the tag +processing transformations. Values of true, yes, 1 +are recognised. + + +

The paved Tag

+ +The paved tag is used to identify whether a highway is paved or not, +this is one of the available highway properties. A paved tag may exist +in the original data but normally the surface tag needs to be +transformed into the paved tag. + + +

The bridge Tag

+ +The bridge tag is used to identify whether a highway is a bridge and +therefore set one of the available properties. + + +

The tunnel Tag

+ +The tunnel tag is used to identify whether a highway is a tunnel and +therefore set one of the available properties. + + +

The oneway Tag

+ +The oneway tag is used to specify that traffic is only allowed to +travel in one direction. + + +

The maxspeed Tag

+ +The maxspeed tag is used to specify the maximum speed limit on the +highway; this is always measured in km/hr in OpenStreetMap data. If the tag +value contains "mph" then it is assumed to be a value in those units and +converted to km/hr. + + +

The maxweight Tag

+ +The maxweight tag is used to specify the maximum weight of any traffic +on the way. In other words this must be set to the heaviest weight allowed on +the way (for example a bridge) in tonnes. If the tag value contains "kg" then +it is assumed that the value is in these units and converted to tonnes. + + +

The maxheight Tag

+ +The maxheight tag is used to specify the maximum height of any traffic +on the way. In other words this must be set to the lowest height of anything +above the way (like a bridge) in metres. If the tag value contains a +measurement in feet or feet and inches then attempts are made to convert this to +metres. + + +

The maxwidth Tag

+ +The maxwidth tag is used to specify the maximum width of any traffic on +the way. This must be set to the minimum width of the contraints at the wayside +in metres. If the tag value contains a measurement in feet or feet and inches +then attempts are made to convert this to metres. + + +

The maxlength Tag

+ +The maxlength tag is used to specify the maximum length of any traffic +on the way (usually from a traffic sign) in metres. If the tag value contains a +measurement in feet or feet and inches then attempts are made to convert this to +metres. + + +

Relation Tags And Attributes

+ +Currently no relation tags or attributes are used. + + +

Tag Transformations

+ +This section describes the set of tag transformations that are contained in the +default configuration file. The configuration file tagging rules are applied in +sequence and this section of the document is arranged in the same order. + + +

Node Tag Transformations

+ +No transformations are applicable since no node tags are recognised. + + +

Way Tag Transformations

+ + +

Highway Defaults

+ +The first part of the tag transformations is to decide on defaults for each type +of highway. This uses the highway tag in the OSM file and maps it into one of +the highway tags that are recognised by Routino, defining the default allowed +transport types and adding a number of properties. + +

+ +The first part of the transformation is to convert the highway tag into one that +is recognised by Routino. + +

+ + + + + + + + + + + + + + + +
Mapping of equivalent highway types
Original tag + Transformed tag +
bridleway + path +
byway + track +
footway + path +
living_street + residential +
minor + unclassified +
pedestrian + path +
road + unclassified +
services + service +
unsurfaced + track +
unpaved + track +
walkway + path +
+ +

+ +The type of highway also determines the defaults for the types of transport +allowed on the highway. The default assumptions are as shown in the table +below. + +

+ + + + + + + + + + + + + + + + +
Transport types on different highway types
Highway + foot + horse + bicycle + wheelchair + moped + motorbike + motorcar + goods + hgv + psv +
motorway + no + no + no + no + no + yes + yes + yes + yes + yes +
trunk + no + no + no + yes + yes + yes + yes + yes + yes + yes +
primary + yes + yes + yes + yes + yes + yes + yes + yes + yes + yes +
secondary + yes + yes + yes + yes + yes + yes + yes + yes + yes + yes +
tertiary + yes + yes + yes + yes + yes + yes + yes + yes + yes + yes +
unclassified + yes + yes + yes + yes + yes + yes + yes + yes + yes + yes +
residential + yes + yes + yes + yes + yes + yes + yes + yes + yes + yes +
service + yes + yes + yes + yes + yes + yes + yes + yes + yes + yes +
track + yes + yes + yes + yes + no + no + no + no + no + no +
cycleway + yes + no + yes + yes + no + no + no + no + no + no +
path + yes + yes (1) + yes + yes (1) + no + no + no + no + no + no +
steps + yes + no + no + no + no + no + no + no + no + no +
+ +

+ + + Note 1: A path allows bicycle or horse access by default only if actually + labelled as a highway of type "bridleway" or certain values of + the designation tag (described below). + + +

+ +Finally for the highway tag a number of properties are added depending on the +highway type. + +

+ + + + + + + + + + + + + + + + +
Properties on different highway types
Highway + Properties +
motorway + paved, oneway, multilane +
trunk + paved +
primary + paved +
secondary + paved +
tertiary + paved +
unclassified + paved +
residential + paved +
service + paved +
track + paved (1) +
cycleway + paved +
path + paved (2) +
steps + +
+ +

+ + + Note 1: A track is paved only if it is tagged as tracktype=grade1. +
+ Note 2: A path is paved only if it was originally tagged as highway=walkway or + highway=pedestrian. +
+ + +

Generic Access Permissions

+ +The access tag is used to specify the default access restrictions on +the way. If the tag value is "no" or "private" then all transport types are +denied access (later tag transformation rules may add specific transport types +back again). + + +

Other Access Permissions

+ +A tag named vehicle means any of the bicycle, moped, +motorbike, motorcar, goods, hgv +and psv transport types. A tag named motor_vehicle is +transformed to mean any vehicle except a bicycle. + +

+ +The designation tag is used as an alternative method of identifying the +legal right of way on a path (in the UK at least). The tag transformations +convert these tags into a set of allowed transport types as shown below. + +

+ + + + + + + + +
Aliasing of designation types
Designation tag + Equivalent access permissions +
bridleway or public_bridleway + foot=yes, wheelchair=yes, horse=yes, bicycle=yes +
restricted_byway + foot=yes, wheelchair=yes, horse=yes, bicycle=yes +
byway + foot=yes, wheelchair=yes, horse=yes, bicycle=yes, moped=yes, motorbike=yes, motorcar=yes +
footpath or public_footpath + foot=yes, wheelchair=yes +
+ + +

Specific Access Permissions

+ +The final part of the access permissions is to use the specific transport type +tags. + +

+ +One tag is recognised for each of the different modes of transport: foot, +horse, bicycle, wheelchair, moped, +motorbike, motorcar, goods, hgv +and psv. These indicate whether the specific type of transport is +allowed on the highway or not. + + +

Highway Properties

+ +If there is a surface tag then the highway is assumed to be unpaved unless the +tag value matches one of the following: paved, asphalt +or concrete. + +

+ +Support for the obsolete paved tag is also provided and the highway is +paved if this is set to a true value. + +

+ +The lanes tag is used to identify whether a highway has multiple lanes +for traffic or not (the number of lanes is not important in this case, only +whether it is more than one) this sets one of the highway properties. + +

+ +The bridge and tunnel tags are copied directly from the input +to the output. + + +

Highway Restrictions

+ +The oneway, maxspeed, maxweight, maxheight, +maxwidth and maxlength are copied directly from the input to +the output without modification. + + +

Highway Names and References

+ +The name and ref tags are copied directly from the input to +the output. + + +

Relation Tag Transformations

+ +No transformations are applicable since no relation tags are recognised. + +
+ + + + + + + + + + + + diff --git a/doc/html/usage.html b/doc/html/usage.html new file mode 100644 index 0000000..dc75888 --- /dev/null +++ b/doc/html/usage.html @@ -0,0 +1,443 @@ + + + + + + +Routino : Usage + + + + + + + + +
+ +

Routino : Usage

+ +
+
+ + + + + +
+ +

Program Usage

+ +There are four programs that make up this software. The first one takes the +planet.osm datafile from OpenStreetMap (or other source of data using the same +formats) and converts it into a local database. The second program uses the +database to determine an optimum route between two points. The third program +allows visualisation of the data and statistics to be extracted. The fourth +program is a test program for the tag transformations. + +

planetsplitter

+ +This program reads in the OSM format XML file and splits it up to create the +database that is used for routing. + +
+Usage: planetsplitter [--help]
+                      [--dir=<dirname>] [--prefix=<name>]
+                      [--slim] [--sort-ram-size=<size>]
+                      [--tmpdir=<dirname>]
+                      [--parse-only | --process-only]
+                      [--max-iterations=<number>]
+                      [--tagging=<filename>]
+                      [<filename.osm> ...]
+
+ +
+
--help +
Prints out the help information. +
--dir=<dirname> +
Sets the directory name in which to save the results. + Defaults to the current directory. +
--prefix=<name> +
Sets the filename prefix for the files that are created. + Defaults to no prefix. +
--slim +
Selects a mode of operation that uses less memory and will therefore work + where virtual memory is very limited or unavailable. Selecting this option + will cause raw data to be held in disk files with only indexes in RAM. Not + using this option will still use disk files but only for sequential access + and the files are memory mapped for random access. +
--sort-ram-size=<size> +
Specifies the amount of RAM (in MB) to use for sorting the data. If not + specified then 64 MB will be used if the '--slim' option is specified or 256 + MB otherwise. +
--tmpdir=<dirname> +
Specifies the name of the directory to store the temporary disk files. If + not specified then it defaults to either the value of the --dir option or the + current directory. +
--parse-only +
Parse the input files and store them in a temporary file but don't process + the data into a routing database. +
--process-only +
Don't read in any files but process the existing temporary file into the + routing database. +
--max-iterations=<number> +
The maximum number of iterations to use when generating super-nodes and + super-segments. Defaults to 10 which is normally enough. +
--tagging=<filename> +
The name of the XML file containing the tagging rules (defaults to + 'tagging.xml' with '--dirname' and '--prefix' options). +
<filename.osm> ... +
Specifies the filename(s) to read data from, by default data is read from + the standard input. +
+ +

+Note: In version 1.4 of Routino the --transport, --not-highway and +--not-property options have been removed. The same functionality can be +achieved by editing the tagging rules file to not output unwwanted data. + +

+Example usage: + +

+./planetsplitter --dir=data --prefix=gb great_britain.osm
+
+ +This will generate the output files 'data/gb-nodes.mem', 'data/gb-segments.mem' +and 'data/gb-ways.mem'. + + +

router

+ +This program performs the calculation of the optimum routes using the database +generated by the planetsplitter program. + +
+Usage: router [--help | --help-profile | --help-profile-xml |
+                        --help-profile-json | --help-profile-perl ]
+              [--dir=<dirname>] [--prefix=<name>]
+              [--profiles=<filename>] [--translations=<filename>]
+              [--exact-nodes-only]
+              [--quiet]
+              [--output-html]
+              [--output-gpx-track] [--output-gpx-route]
+              [--output-text] [--output-text-all]
+              [--output-none]
+              [--profile=<name>]
+              [--transport=<transport>]
+              [--shortest | --quickest]
+              --lon1=<longitude> --lat1=<latitude>
+              --lon2=<longitude> --lon2=<latitude>
+              [ ... --lon99=<longitude> --lon99=<latitude>]
+              [--highway-<highway>=<preference> ...]
+              [--speed-<highway>=<speed> ...]
+              [--property-<property>=<preference> ...]
+              [--oneway=(0|1)]
+              [--weight=<weight>]
+              [--height=<height>] [--width=<width>] [--length=<length>]
+
+ +
+
--help +
Prints out the help information. +
--help-profile +
Prints out the selected transport profile (type, speed limits, highway + preferences etc.) +
--help-profile-xml +
Prints out all the loaded profiles as an XML file in the same format that + can be loaded in. +
--help-profile-json +
Prints out all the loaded profiles in JavaScript Object Notation (JSON) + format for use in the interactive webpage. +
--help-profile-perl +
Prints out all the loaded profiles as a Perl object for use in the router + CGI. +
--dir=<dirname> +
Sets the directory name in which to read the local database. + Defaults to the current directory. +
--prefix=<name> +
Sets the filename prefix for the files in the local database. + Defaults to no prefix. +
--profiles=<filename> +
Sets the filename containing the list of profiles in XML format. If the + file doesn't exist then dirname, prefix and "profiles.xml" will be combined + and used, if that doesn't exist then the command line must contain all + relevant profile information. +
--translations=<filename> +
Sets the filename containing the list of translations in XML format for + the output files. If the file doesn't exist then dirname, prefix and + "translations.xml" will be combined and used, if that doesn't exist then no + file will be read and no language can be selected. +
--exact-nodes-only +
When processing the specified latitude and longitude points only select + the nearest node instead of finding the nearest point within a segment + (quicker but less accurate unless the points are already near nodes). +
--quiet +
Don't generate any screen output while running (useful for running in a script). +
--language=<lang> +
Select the language specified from the file of translations. If this + option is not given and the file exists then the first language in the file + will be used. If this option is not given and no file exists the + compiled-in default language (English) will be used. +
--output-html +
--output-gpx-track +
--output-gpx-route +
--output-text +
--output-text-all +
Generate the selected output file formats (HTML, GPX track file, GPX route + file, plain text route and/or plain text with all nodes). If no output is + specified then all are generated, specifying any automatically disables those + not specified. +
--output-none +
Do not generate any output or read in any translations files. +
--profile=<name> +
Specifies the name of the profile to use. +
--transport=<transport> +
Select the type of transport to use, <transport> can be set to: +
    +
  • foot = Foot +
  • horse = Horse +
  • wheelchair = Wheelchair +
  • bicycle = Bicycle +
  • moped = Moped (Small motorbike, limited speed) +
  • motorbike = Motorbike +
  • motorcar = Motorcar +
  • goods = Goods (Small lorry, van) +
  • hgv = HGV (Heavy Goods Vehicle - large lorry) +
  • psv = PSV (Public Service Vehicle - bus, coach) +
+ Defaults to 'motorcar', this option also selects the default profile + information if the '--profile' option is not given and a profile matching + the transport name is found. +
--shortest +
Find the shortest route between the waypoints. +
--quickest +
Find the quickest route between the waypoints. +
--lon1=<longitude>, --lat1=<latitude> +
--lon2=<longitude>, --lat2=<latitude> +
... --lon99=<longitude>, --lat99=<latitude> +
The location of the waypoints that make up the start, middle and end + points of the route. Up to 99 waypoints can be specified and the route will + pass through each of the specified ones in sequence. The algorithm will use + the closest node or point within a segment that allows the specified traffic + type. +
--highway-<highway>=<preference> +
Selects the percentage preference for using each particular type of + highway. The value of <highway> can be selected from: +
    +
  • motorway = Motorway +
  • trunk = Trunk +
  • primary = Primary +
  • secondary = Secondary +
  • tertiary = Tertiary +
  • unclassified = Unclassified +
  • residential = Residential +
  • service = Service +
  • track = Track +
  • cycleway = Cycleway +
  • path = Path +
  • steps = Steps +
+ Default value depends on the profile selected by the --transport option. +
--speed-<highway>=<speed> +
Selects the speed limit in km/hour for each type of highway. Default + value depends on the profile selected by the --transport option. +
--property-<property>=<preference> +
Selects the percentage preference for using each particular highway + property + The value of <property> can be selected from: +
    +
  • paved = Paved (suitable for normal wheels) +
  • multilane = Multiple lanes +
  • bridge = Bridge +
  • tunnel = Tunnel +
+ Default value depends on the profile selected by the --transport option. +
--oneway=[0|1] +
Selects if the direction of oneway streets are to be obeyed (useful to not + obey them when walking). Default value depends on the profile selected by + the --transport option. +
--weight=<weight> +
Specifies the weight of the mode of transport in tonnes; ensures that the + weight limit on the highway is not exceeded. Default value depends on the + profile selected by the --transport option. +
--height=<height> +
Specifies the height of the mode of transport in metres; ensures that the + height limit on the highway is not exceeded. Default value depends on the + profile selected by the --transport option. +
--width=<width> +
Specifies the width of the mode of transport in metres; ensures that the + width limit on the highway is not exceeded. Default value depends on the + profile selected by the --transport option. +
--length=<length> +
Specifies the length of the mode of transport in metres; ensures that the + length limit on the highway is not exceeded. Default value depends on the + profile selected by the --transport option. +
+ +

+The meaning of the <preference> parameter in the command line options is +slightly different for the highway preferences and the property preferences. +For the highway preference consider the choice between two possible highways +between the start and finish when looking for the shortest route. If highway A +has a preference of 100% and highway B has a preference of 90% then highway A +will be chosen even if it is up to 11% longer (100/90 = 111%). For the highway +properties each highway either has a particular property or not. If the +preference for highways with the property is 60% then the preference for +highways without the property is 40%. The overall preference for the highway is +the product of the highway preference and the preference for highways with (or +without) each property that the highway has (or doesn't have). + +

+Example usage (motorbike journey, scenic route, not very fast): + +

+./router --dir=data --prefix=gb --transport=motorbike --highway-motorway=0 \
+         --highway-trunk=0 --speed-primary=80 --speed-secondary=80 --quickest
+
+ +This will use the files 'data/gb-nodes.mem', 'data/gb-segments.mem' and +'data/gb-ways.mem' to find the quickest route by motorbike not using motorways +or trunk roads and not exceeding 80 km/hr. + + +

filedumper

+ +This program is used to extract statistics from the database, extract particular +information for visualisation purposes or for dumping the database contents. + +
+Usage: filedumper [--help]
+                  [--dir=<dirname>] [--prefix=<name>]
+                  [--statistics]
+                  [--visualiser --latmin=<latmin> --latmax=<latmax>
+                                --lonmin=<lonmin> --lonmax=<lonmax>
+                                --data=<data-type>]
+                  [--dump [--node=<node> ...]
+                          [--segment=<segment> ...]
+                          [--way=<way> ...]]
+                  [--dump-osm [--no-super]
+                              [--latmin=<latmin> --latmax=<latmax>
+                               --lonmin=<lonmin> --lonmax=<lonmax>]]
+
+ +
+
--help +
Prints out the help information. +
--dir=<dirname> +
Sets the directory name in which to read the local database. + Defaults to the current directory. +
--prefix=<name> +
Sets the filename prefix for the files in the local database. +
--statistics +
Prints out statistics about the database files. +
--visualiser +
Selects a data visualiser mode which will output a set of data according + to the other parameters below. +
+
--latmin=<latmin> --latmax=<latmax> +
The range of latitudes to print the data for. +
--lonmin=<lonmin> --lonmax=<lonmax> +
The range of longitudes to print the data for. +
--data=<data-type> +
The type of data to output, <data-type> can be selected from: +
    +
  • junctions = segment count at each junction. +
  • super = super-node and super-segments. +
  • oneway = oneway segments. +
  • speed = speed limits. +
  • weight = weight limits. +
  • height = height limits. +
  • width = width limits. +
  • length = length limits. +
+
+
--dump +
Selects a data dumping mode which allows looking at individual items in + the databases (specifying 'all' instead of a number dumps all of them). +
+
--node=<node> +
Prints the information about the selected node number (internal + number, not the node id number in the original source file). +
--segment=<segment> +
Prints the information about the selected segment number. +
--way=<way> +
Prints the information about the selected way number (internal + number, not the way id number in the original source file). +
+
--osm-dump +
Dumps the contents of the database as an OSM format XML file, the whole + database will be dumped unless the latitude and longitude ranges are + specified. +
+
--no-super +
The super segments will not be output. +
--latmin=<latmin> --latmax=<latmax> +
The range of latitudes to dump the data for. +
--lonmin=<lonmin> --lonmax=<lonmax> +
The range of longitudes to dump the data for. +
+
+ + +

tagmodifier

+ +This program is used to run the tag transformation process on an OSM XML file +for test purposes. + +
+Usage: tagmodifier [--help]
+                   [--tagging=<filename>]
+                   [<filename.osm>]
+
+ +
+
--help +
Prints out the help information. +
--tagging=<filename> +
The name of the XML file containing the tagging rules (defaults to + 'tagging.xml' in the current directory). +
<filename.osm> ... +
Specifies the filename to read data from, by default data is read from + the standard input. +
+ +
+ + + + + + + + + + + + diff --git a/src/Makefile b/src/Makefile new file mode 100644 index 0000000..49e583e --- /dev/null +++ b/src/Makefile @@ -0,0 +1,154 @@ +# $Header: /home/amb/routino/src/RCS/Makefile,v 1.36 2010/07/09 17:43:00 amb Exp $ +# +# Source code Makefile +# +# Part of the Routino routing software. +# +# This file Copyright 2008-2010 Andrew M. Bishop +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# + +# Programs + +CC=gcc +LD=gcc + +LEX=flex + +# Program options + +CFLAGS=-Wall -Wmissing-prototypes +#CFLAGS+= -Wextra -pedantic -std=c99 +LDFLAGS=-lm -lc + +CFLAGS+= -O3 +#CFLAGS+= -O0 -g +#CFLAGS+= -pg +#CFLAGS+= --coverage + +LDFLAGS+= +#LDFLAGS+= -pg -static +#LDFLAGS+= --coverage + +LEXFLAGS= + +# Required to use stdio with files > 2GiB on 32-bit system. + +FLAGS64=-D_FILE_OFFSET_BITS=64 + +# Compilation targets + +C=$(wildcard *.c) +D=$(foreach f,$(C),$(addprefix .deps/,$(addsuffix .d,$(basename $f)))) + +EXE=planetsplitter router filedumper tagmodifier + +WEBDIR=../web/bin + +######## + +all : $(EXE) + -@[ -d $(WEBDIR) ] && \ + for file in $(EXE); do \ + if [ ! -f $(WEBDIR)/$$file ] || [ $$file -nt $(WEBDIR)/$$file ]; then \ + echo cp $$file $(WEBDIR) ;\ + cp -f $$file $(WEBDIR) ;\ + fi ;\ + done + @cd xml && $(MAKE) CC="$(CC)" LD="$(LD)" CFLAGS="$(CFLAGS)" LDFLAGS="$(LDFLAGS)" + +######## + +PLANETSPLITTER_OBJ=planetsplitter.o \ + nodesx.o segmentsx.o waysx.o superx.o \ + ways.o types.o \ + files.o \ + results.o queue.o sorting.o \ + xmlparse.o tagging.o osmparser.o + +planetsplitter : $(PLANETSPLITTER_OBJ) + $(LD) $(PLANETSPLITTER_OBJ) -o $@ $(LDFLAGS) + +######## + +ROUTER_OBJ=router.o \ + nodes.o segments.o ways.o types.o \ + files.o profiles.o xmlparse.o \ + optimiser.o output.o results.o queue.o translations.o + +router : $(ROUTER_OBJ) + $(LD) $(ROUTER_OBJ) -o $@ $(LDFLAGS) + +######## + +FILEDUMPER_OBJ=filedumper.o \ + nodes.o segments.o ways.o types.o \ + files.o xmlparse.o \ + visualiser.o + +filedumper : $(FILEDUMPER_OBJ) + $(LD) $(FILEDUMPER_OBJ) -o $@ $(LDFLAGS) + +######## + +TAGMODIFIER_OBJ=tagmodifier.o \ + files.o \ + xmlparse.o tagging.o + +tagmodifier : $(TAGMODIFIER_OBJ) + $(LD) $(TAGMODIFIER_OBJ) -o $@ $(LDFLAGS) + +######## + +xmlparse.c : xmlparse.l + $(LEX) $(LEXFLAGS) $< + -@mv lex.yy.c xmlparse.c + @echo Created xmlparse.c + +######## + +%.o : %.c + $(CC) -c $(CFLAGS) $(FLAGS64) $< -o $@ -MMD -MP -MF $(addprefix .deps/,$(addsuffix .d,$(basename $<))) + +######## + +clean: + rm -f *.o + rm -f *~ + rm -f xmlparse.c + cd xml && $(MAKE) clean + +######## + +distclean: clean + -[ -d ../web/bin ] && cd ../web/bin/ && rm -f $(EXE) + -rm -f $(EXE) + -rm -f $(D) + -rm -fr .deps + cd xml && $(MAKE) distclean + +######## + +.deps : .FORCE + @[ -d .deps ] || mkdir $@ + +$(D) : .deps + @touch $@ + +include $(D) + +######## + +.FORCE : diff --git a/src/filedumper.c b/src/filedumper.c new file mode 100644 index 0000000..463ab19 --- /dev/null +++ b/src/filedumper.c @@ -0,0 +1,674 @@ +/*************************************** + $Header: /home/amb/routino/src/RCS/filedumper.c,v 1.43 2010/05/30 12:52:16 amb Exp $ + + Memory file dumper. + + Part of the Routino routing software. + ******************/ /****************** + This file Copyright 2008-2010 Andrew M. Bishop + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + ***************************************/ + + +#include +#include +#include +#include +#include +#include + +#include "types.h" +#include "functions.h" +#include "visualiser.h" +#include "nodes.h" +#include "segments.h" +#include "ways.h" +#include "xmlparse.h" + + +/* Local functions */ + +static void print_node(Nodes* nodes,index_t item); +static void print_segment(Segments *segments,index_t item); +static void print_way(Ways *ways,index_t item); + +static void print_head_osm(void); +static void print_node_osm(Nodes* nodes,index_t item); +static void print_segment_osm(Segments *segments,index_t item,Ways *ways); +static void print_tail_osm(void); + +static char *RFC822Date(time_t t); + +static void print_usage(int detail); + + +/*++++++++++++++++++++++++++++++++++++++ + The main program for the file dumper. + ++++++++++++++++++++++++++++++++++++++*/ + +int main(int argc,char** argv) +{ + Nodes *OSMNodes; + Segments *OSMSegments; + Ways *OSMWays; + int arg; + char *dirname=NULL,*prefix=NULL; + char *nodes_filename,*segments_filename,*ways_filename; + int option_statistics=0; + int option_visualiser=0,coordcount=0; + double latmin=0,latmax=0,lonmin=0,lonmax=0; + char *option_data=NULL; + int option_dump=0; + int option_dump_osm=0,option_no_super=0; + + /* Parse the command line arguments */ + + for(arg=1;argnumber); + printf("Number(super)=%9d\n",OSMNodes->snumber); + printf("\n"); + + printf("Lat bins= %4d\n",OSMNodes->latbins); + printf("Lon bins= %4d\n",OSMNodes->lonbins); + printf("\n"); + + printf("Lat zero=%5d (%8.4f deg)\n",OSMNodes->latzero,radians_to_degrees(latlong_to_radians(bin_to_latlong(OSMNodes->latzero)))); + printf("Lon zero=%5d (%8.4f deg)\n",OSMNodes->lonzero,radians_to_degrees(latlong_to_radians(bin_to_latlong(OSMNodes->lonzero)))); + + /* Examine the segments */ + + printf("\n"); + printf("Segments\n"); + printf("--------\n"); + printf("\n"); + + printf("sizeof(Segment)=%9d Bytes\n",sizeof(Segment)); + printf("Number(total) =%9d\n",OSMSegments->number); + printf("Number(super) =%9d\n",OSMSegments->snumber); + printf("Number(normal) =%9d\n",OSMSegments->nnumber); + + /* Examine the ways */ + + printf("\n"); + printf("Ways\n"); + printf("----\n"); + printf("\n"); + + printf("sizeof(Way) =%9d Bytes\n",sizeof(Way)); + printf("Number(compacted)=%9d\n",OSMWays->number); + printf("Number(original) =%9d\n",OSMWays->onumber); + printf("\n"); + + printf("Total names =%9ld Bytes\n",(long)buf.st_size-sizeof(Ways)-OSMWays->number*sizeof(Way)); + printf("\n"); + + printf("Included transports: %s\n",AllowedNameList(OSMWays->allow)); + printf("Included properties: %s\n",PropertiesNameList(OSMWays->props)); + } + + /* Print out internal data */ + + if(option_dump) + { + index_t item; + + for(arg=1;argnumber;item++) + print_node(OSMNodes,item); + } + else if(!strncmp(argv[arg],"--node=",7)) + { + item=atoi(&argv[arg][7]); + + if(item>=0 && itemnumber) + print_node(OSMNodes,item); + else + printf("Invalid node number; minimum=0, maximum=%d.\n",OSMNodes->number-1); + } + else if(!strcmp(argv[arg],"--segment=all")) + { + for(item=0;itemnumber;item++) + print_segment(OSMSegments,item); + } + else if(!strncmp(argv[arg],"--segment=",10)) + { + item=atoi(&argv[arg][10]); + + if(item>=0 && itemnumber) + print_segment(OSMSegments,item); + else + printf("Invalid segment number; minimum=0, maximum=%d.\n",OSMSegments->number-1); + } + else if(!strcmp(argv[arg],"--way=all")) + { + for(item=0;itemnumber;item++) + print_way(OSMWays,item); + } + else if(!strncmp(argv[arg],"--way=",6)) + { + item=atoi(&argv[arg][6]); + + if(item>=0 && itemnumber) + print_way(OSMWays,item); + else + printf("Invalid way number; minimum=0, maximum=%d.\n",OSMWays->number-1); + } + } + + /* Print out internal data in XML format */ + + if(option_dump_osm) + { + if(coordcount>0 && coordcount!=4) + { + fprintf(stderr,"The --dump-osm option must have all of --latmin, --latmax, --lonmin, --lonmax or none.\n"); + exit(1); + } + + print_head_osm(); + + if(coordcount) + { + int32_t latminbin=latlong_to_bin(radians_to_latlong(latmin))-OSMNodes->latzero; + int32_t latmaxbin=latlong_to_bin(radians_to_latlong(latmax))-OSMNodes->latzero; + int32_t lonminbin=latlong_to_bin(radians_to_latlong(lonmin))-OSMNodes->lonzero; + int32_t lonmaxbin=latlong_to_bin(radians_to_latlong(lonmax))-OSMNodes->lonzero; + int latb,lonb,llbin; + index_t node; + + /* Loop through all of the nodes. */ + + for(latb=latminbin;latb<=latmaxbin;latb++) + for(lonb=lonminbin;lonb<=lonmaxbin;lonb++) + { + llbin=lonb*OSMNodes->latbins+latb; + + if(llbin<0 || llbin>(OSMNodes->latbins*OSMNodes->lonbins)) + continue; + + for(node=OSMNodes->offsets[llbin];nodeoffsets[llbin+1];node++) + { + double lat=latlong_to_radians(bin_to_latlong(OSMNodes->latzero+latb)+off_to_latlong(OSMNodes->nodes[node].latoffset)); + double lon=latlong_to_radians(bin_to_latlong(OSMNodes->lonzero+lonb)+off_to_latlong(OSMNodes->nodes[node].lonoffset)); + + if(lat>latmin && latlonmin && lonOtherNode(segment,node)) + if(!option_no_super || IsNormalSegment(segment)) + print_segment_osm(OSMSegments,IndexSegment(OSMSegments,segment),OSMWays); + + segment=NextSegment(OSMSegments,segment,node); + } + } + } + } + } + else + { + index_t item; + + for(item=0;itemnumber;item++) + print_node_osm(OSMNodes,item); + + for(item=0;itemnumber;item++) + if(!option_no_super || IsNormalSegment(LookupSegment(OSMSegments,item))) + print_segment_osm(OSMSegments,item,OSMWays); + } + + print_tail_osm(); + } + + return(0); +} + + +/*++++++++++++++++++++++++++++++++++++++ + Print out the contents of a node from the routing database. + + Nodes *nodes The set of nodes to use. + + index_t item The node index to print. + ++++++++++++++++++++++++++++++++++++++*/ + +static void print_node(Nodes* nodes,index_t item) +{ + Node *node=LookupNode(nodes,item); + double latitude,longitude; + + GetLatLong(nodes,item,&latitude,&longitude); + + printf("Node %d\n",item); + printf(" firstseg=%d\n",SEGMENT(node->firstseg)); + printf(" latoffset=%d lonoffset=%d (latitude=%.6f longitude=%.6f)\n",node->latoffset,node->lonoffset,radians_to_degrees(latitude),radians_to_degrees(longitude)); + if(IsSuperNode(nodes,item)) + printf(" Super-Node\n"); +} + + +/*++++++++++++++++++++++++++++++++++++++ + Print out the contents of a segment from the routing database. + + Segments *segments The set of segments to use. + + index_t item The segment index to print. + ++++++++++++++++++++++++++++++++++++++*/ + +static void print_segment(Segments *segments,index_t item) +{ + Segment *segment=LookupSegment(segments,item); + + printf("Segment %d\n",item); + printf(" node1=%d node2=%d\n",segment->node1,segment->node2); + printf(" next2=%d\n",segment->next2); + printf(" way=%d\n",segment->way); + printf(" distance=%d (%.3f km)\n",DISTANCE(segment->distance),distance_to_km(DISTANCE(segment->distance))); + if(IsSuperSegment(segment) && IsNormalSegment(segment)) + printf(" Super-Segment AND normal Segment\n"); + else if(IsSuperSegment(segment) && !IsNormalSegment(segment)) + printf(" Super-Segment\n"); + if(IsOnewayTo(segment,segment->node1)) + printf(" One-Way from node2 to node1\n"); + if(IsOnewayTo(segment,segment->node2)) + printf(" One-Way from node1 to node2\n"); +} + + +/*++++++++++++++++++++++++++++++++++++++ + Print out the contents of a way from the routing database. + + Ways *ways The set of ways to use. + + index_t item The way index to print. + ++++++++++++++++++++++++++++++++++++++*/ + +static void print_way(Ways *ways,index_t item) +{ + Way *way=LookupWay(ways,item); + + printf("Way %d\n",item); + printf(" name=%s\n",WayNameHighway(ways,way)); + printf(" type=%02x (%s%s%s)\n",way->type,HighwayName(HIGHWAY(way->type)),way->type&Way_OneWay?",One-Way":"",way->type&Way_Roundabout?",Roundabout":""); + printf(" allow=%02x (%s)\n",way->allow,AllowedNameList(way->allow)); + if(way->props) + printf(" props=%02x (%s)\n",way->props,PropertiesNameList(way->props)); + if(way->speed) + printf(" speed=%d (%d km/hr)\n",way->speed,speed_to_kph(way->speed)); + if(way->weight) + printf(" weight=%d (%.1f tonnes)\n",way->weight,weight_to_tonnes(way->weight)); + if(way->height) + printf(" height=%d (%.1f m)\n",way->height,height_to_metres(way->height)); + if(way->width) + printf(" width=%d (%.1f m)\n",way->width,width_to_metres(way->width)); + if(way->length) + printf(" length=%d (%.1f m)\n",way->length,length_to_metres(way->length)); +} + + +/*++++++++++++++++++++++++++++++++++++++ + Print out a header in OSM XML format. + ++++++++++++++++++++++++++++++++++++++*/ + +static void print_head_osm(void) +{ + printf("\n"); + printf("\n"); +} + + +/*++++++++++++++++++++++++++++++++++++++ + Print out the contents of a node from the routing database in OSM XML format. + + Nodes *nodes The set of nodes to use. + + index_t item The node index to print. + ++++++++++++++++++++++++++++++++++++++*/ + +static void print_node_osm(Nodes* nodes,index_t item) +{ + double latitude,longitude; + + GetLatLong(nodes,item,&latitude,&longitude); + + if(IsSuperNode(nodes,item)) + { + printf(" \n",(unsigned long)item+1,radians_to_degrees(latitude),radians_to_degrees(longitude)); + printf(" \n"); + printf(" \n"); + } + else + printf(" \n",(unsigned long)item+1,radians_to_degrees(latitude),radians_to_degrees(longitude)); +} + + +/*++++++++++++++++++++++++++++++++++++++ + Print out the contents of a segment from the routing database as a way in OSM XML format. + + Segments *segments The set of segments to use. + + index_t item The segment index to print. + + Ways *ways The set of ways to use. + ++++++++++++++++++++++++++++++++++++++*/ + +static void print_segment_osm(Segments *segments,index_t item,Ways *ways) +{ + Segment *segment=LookupSegment(segments,item); + Way *way=LookupWay(ways,segment->way); + int i; + + printf(" \n",(unsigned long)item+1); + + if(IsOnewayTo(segment,segment->node1)) + { + printf(" \n",(unsigned long)segment->node2+1); + printf(" \n",(unsigned long)segment->node1+1); + } + else + { + printf(" \n",(unsigned long)segment->node1+1); + printf(" \n",(unsigned long)segment->node2+1); + } + + if(IsSuperSegment(segment)) + printf(" \n"); + if(IsNormalSegment(segment)) + printf(" \n"); + + if(way->type & Way_OneWay) + printf(" \n"); + if(way->type & Way_Roundabout) + printf(" \n"); + + printf(" \n",HighwayName(HIGHWAY(way->type))); + + if(IsNormalSegment(segment) && WayNamed(ways,way)) + printf(" \n",ParseXML_Encode_Safe_XML(WayNameHighway(ways,way))); + + for(i=1;iallow & ALLOWED(i)) + printf(" \n",TransportName(i)); + + for(i=1;iprops & PROPERTIES(i)) + printf(" \n",PropertyName(i)); + + if(way->speed) + printf(" \n",speed_to_kph(way->speed)); + + if(way->weight) + printf(" \n",weight_to_tonnes(way->weight)); + if(way->height) + printf(" \n",height_to_metres(way->height)); + if(way->width) + printf(" \n",width_to_metres(way->width)); + if(way->length) + printf(" \n",length_to_metres(way->length)); + + printf(" \n"); +} + + +/*++++++++++++++++++++++++++++++++++++++ + Print out a tail in OSM XML format. + ++++++++++++++++++++++++++++++++++++++*/ + +static void print_tail_osm(void) +{ + printf("\n"); +} + + +/*+ Conversion from time_t to date string (day of week). +*/ +static const char* const weekdays[7]={"Sun","Mon","Tue","Wed","Thu","Fri","Sat"}; + +/*+ Conversion from time_t to date string (month of year). +*/ +static const char* const months[12]={"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"}; + + +/*++++++++++++++++++++++++++++++++++++++ + Convert the time into an RFC 822 compliant date. + + char *RFC822Date Returns a pointer to a fixed string containing the date. + + time_t t The time. + ++++++++++++++++++++++++++++++++++++++*/ + +static char *RFC822Date(time_t t) +{ + static char value[32]; + char weekday[4]; + char month[4]; + struct tm *tim; + + tim=gmtime(&t); + + strcpy(weekday,weekdays[tim->tm_wday]); + strcpy(month,months[tim->tm_mon]); + + /* Sun, 06 Nov 1994 08:49:37 GMT ; RFC 822, updated by RFC 1123 */ + + sprintf(value,"%3s, %02d %3s %4d %02d:%02d:%02d %s", + weekday, + tim->tm_mday, + month, + tim->tm_year+1900, + tim->tm_hour, + tim->tm_min, + tim->tm_sec, + "GMT" + ); + + return(value); +} + + +/*++++++++++++++++++++++++++++++++++++++ + Print out the usage information. + + int detail The level of detail to use - 0 = low, 1 = high. + ++++++++++++++++++++++++++++++++++++++*/ + +static void print_usage(int detail) +{ + fprintf(stderr, + "Usage: filedumper [--help]\n" + " [--dir=] [--prefix=]\n" + " [--statistics]\n" + " [--visualiser --latmin= --latmax=\n" + " --lonmin= --lonmax=\n" + " --data=]\n" + " [--dump [--node= ...]\n" + " [--segment= ...]\n" + " [--way= ...]]\n" + " [--dump-osm [--no-super]\n" + " [--latmin= --latmax=\n" + " --lonmin= --lonmax=]]\n"); + + if(detail) + fprintf(stderr, + "\n" + "--help Prints this information.\n" + "\n" + "--dir= The directory containing the routing database.\n" + "--prefix= The filename prefix for the routing database.\n" + "\n" + "--statistics Print statistics about the routing database.\n" + "\n" + "--visualiser Extract selected data from the routing database:\n" + " --latmin= * the minimum latitude (degrees N).\n" + " --latmax= * the maximum latitude (degrees N).\n" + " --lonmin= * the minimum longitude (degrees E).\n" + " --lonmax= * the maximum longitude (degrees E).\n" + " --data= * the type of data to select.\n" + "\n" + " can be selected from:\n" + " junctions = segment count at each junction.\n" + " super = super-node and super-segments.\n" + " oneway = oneway segments.\n" + " speed = speed limits.\n" + " weight = weight limits.\n" + " height = height limits.\n" + " width = width limits.\n" + " length = length limits.\n" + "\n" + "--dump Dump selected contents of the database.\n" + " --node= * the node with the selected number.\n" + " --segment= * the segment with the selected number.\n" + " --way= * the way with the selected number.\n" + " Use 'all' instead of a number to get all of them.\n" + "\n" + "--dump-osm Dump all or part of the database as an XML file.\n" + " --no-super * exclude the super-segments.\n" + " --latmin= * the minimum latitude (degrees N).\n" + " --latmax= * the maximum latitude (degrees N).\n" + " --lonmin= * the minimum longitude (degrees E).\n" + " --lonmax= * the maximum longitude (degrees E).\n"); + + exit(!detail); +} diff --git a/src/files.c b/src/files.c new file mode 100644 index 0000000..ce595ab --- /dev/null +++ b/src/files.c @@ -0,0 +1,372 @@ +/*************************************** + $Header: /home/amb/routino/src/RCS/files.c,v 1.18 2010/03/29 18:20:06 amb Exp $ + + Functions to handle files. + + Part of the Routino routing software. + ******************/ /****************** + This file Copyright 2008-2010 Andrew M. Bishop + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + ***************************************/ + + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "functions.h" + + +/*+ A structure to contain the list of memory mapped files. +*/ +struct mmapinfo +{ + const char *filename; /*+ The name of the file (the index of the list). +*/ + int fd; /*+ The file descriptor used when it was opened. +*/ + void *address; /*+ The address the file was mapped to. +*/ + size_t length; /*+ The length of the file. +*/ +}; + +/*+ The list of memory mapped files. +*/ +static struct mmapinfo *mappedfiles; + +/*+ The number of mapped files. +*/ +static int nmappedfiles=0; + + +/*++++++++++++++++++++++++++++++++++++++ + Return a filename composed of the dirname, prefix and filename. + + char *FileName Returns an allocated filename. + + const char *dirname The directory name. + + const char *prefix The file prefix. + + const char *name The filename. + ++++++++++++++++++++++++++++++++++++++*/ + +char *FileName(const char *dirname,const char *prefix, const char *name) +{ + char *filename=(char*)malloc((dirname?strlen(dirname):0)+1+(prefix?strlen(prefix):0)+1+strlen(name)); + + sprintf(filename,"%s%s%s%s%s",dirname?dirname:"",dirname?"/":"",prefix?prefix:"",prefix?"-":"",name); + + return(filename); +} + + +/*++++++++++++++++++++++++++++++++++++++ + Open a file and map it into memory. + + void *MapFile Returns the address of the file or exits in case of an error. + + const char *filename The name of the file to open. + ++++++++++++++++++++++++++++++++++++++*/ + +void *MapFile(const char *filename) +{ + int fd; + off_t size; + void *address; + + /* Open the file and get its size */ + + fd=ReOpenFile(filename); + + size=SizeFile(filename); + + /* Map the file */ + + address=mmap(NULL,size,PROT_READ,MAP_SHARED,fd,0); + + if(address==MAP_FAILED) + { + close(fd); + + fprintf(stderr,"Cannot mmap file '%s' [%s].\n",filename,strerror(errno)); + exit(EXIT_FAILURE); + } + + mappedfiles=(struct mmapinfo*)realloc((void*)mappedfiles,(nmappedfiles+1)*sizeof(struct mmapinfo)); + + mappedfiles[nmappedfiles].filename=filename; + mappedfiles[nmappedfiles].fd=fd; + mappedfiles[nmappedfiles].address=address; + mappedfiles[nmappedfiles].length=size; + + nmappedfiles++; + + return(address); +} + + +/*++++++++++++++++++++++++++++++++++++++ + Unmap a file. + + void *UnmapFile Returns NULL (for similarity to the MapFile function). + + const char *filename The name of the file when it was opened. + ++++++++++++++++++++++++++++++++++++++*/ + +void *UnmapFile(const char *filename) +{ + int i; + + for(i=0;ii) + memmove(&mappedfiles[i],&mappedfiles[i+1],(nmappedfiles-i)*sizeof(struct mmapinfo)); + + return(NULL); +} + + +/*++++++++++++++++++++++++++++++++++++++ + Open a new file on disk for writing to. + + int OpenFile Returns the file descriptor if OK or exits in case of an error. + + const char *filename The name of the file to create. + ++++++++++++++++++++++++++++++++++++++*/ + +int OpenFile(const char *filename) +{ + int fd; + + /* Open the file */ + + fd=open(filename,O_WRONLY|O_CREAT|O_TRUNC,S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH); + + if(fd<0) + { + fprintf(stderr,"Cannot open file '%s' for writing [%s].\n",filename,strerror(errno)); + exit(EXIT_FAILURE); + } + + return(fd); +} + + +/*++++++++++++++++++++++++++++++++++++++ + Open a new file on disk for reading and appending. + + int AppendFile Returns the file descriptor if OK or exits in case of an error. + + const char *filename The name of the file to create. + ++++++++++++++++++++++++++++++++++++++*/ + +int AppendFile(const char *filename) +{ + int fd; + + /* Open the file */ + + fd=open(filename,O_RDWR|O_CREAT|O_APPEND,S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH); + + if(fd<0) + { + fprintf(stderr,"Cannot open file '%s' for appending [%s].\n",filename,strerror(errno)); + exit(EXIT_FAILURE); + } + + return(fd); +} + + +/*++++++++++++++++++++++++++++++++++++++ + Open an existing file on disk for reading from. + + int ReOpenFile Returns the file descriptor if OK or exits in case of an error. + + const char *filename The name of the file to open. + ++++++++++++++++++++++++++++++++++++++*/ + +int ReOpenFile(const char *filename) +{ + int fd; + + /* Open the file */ + + fd=open(filename,O_RDONLY); + + if(fd<0) + { + fprintf(stderr,"Cannot open file '%s' for reading [%s].\n",filename,strerror(errno)); + exit(EXIT_FAILURE); + } + + return(fd); +} + + +/*++++++++++++++++++++++++++++++++++++++ + Write data to a file on disk. + + int WriteFile Returns 0 if OK or something else in case of an error. + + int fd The file descriptor to write to. + + const void *address The address of the data to be written from. + + size_t length The length of data to write. + ++++++++++++++++++++++++++++++++++++++*/ + +int WriteFile(int fd,const void *address,size_t length) +{ + /* Write the data */ + + if(write(fd,address,length)!=length) + return(-1); + + return(0); +} + + +/*++++++++++++++++++++++++++++++++++++++ + Read data from a file on disk. + + int ReadFile Returns 0 if OK or something else in case of an error. + + int fd The file descriptor to read from. + + void *address The address of the data to be read into. + + size_t length The length of data to read. + ++++++++++++++++++++++++++++++++++++++*/ + +int ReadFile(int fd,void *address,size_t length) +{ + /* Read the data */ + + if(read(fd,address,length)!=length) + return(-1); + + return(0); +} + + +/*++++++++++++++++++++++++++++++++++++++ + Get the size of a file. + + off_t SizeFile Returns the file size. + + const char *filename The name of the file to check. + ++++++++++++++++++++++++++++++++++++++*/ + +off_t SizeFile(const char *filename) +{ + struct stat buf; + + if(stat(filename,&buf)) + { + fprintf(stderr,"Cannot stat file '%s' [%s].\n",filename,strerror(errno)); + exit(EXIT_FAILURE); + } + + return(buf.st_size); +} + + +/*++++++++++++++++++++++++++++++++++++++ + Check if a file exists. + + int ExistsFile Returns 1 if the file exists and 0 if not. + + const char *filename The name of the file to check. + ++++++++++++++++++++++++++++++++++++++*/ + +int ExistsFile(const char *filename) +{ + struct stat buf; + + if(stat(filename,&buf)) + return(0); + else + return(1); +} + + +/*++++++++++++++++++++++++++++++++++++++ + Seek to a position in a file on disk. + + int SeekFile Returns 0 if OK or something else in case of an error. + + int fd The file descriptor to seek within. + + off_t position The position to seek to. + ++++++++++++++++++++++++++++++++++++++*/ + +int SeekFile(int fd,off_t position) +{ + /* Seek the data */ + + if(lseek(fd,position,SEEK_SET)!=position) + return(-1); + + return(0); +} + + +/*++++++++++++++++++++++++++++++++++++++ + Close a file on disk. + + int fd The file descriptor to close. + ++++++++++++++++++++++++++++++++++++++*/ + +void CloseFile(int fd) +{ + close(fd); +} + + +/*++++++++++++++++++++++++++++++++++++++ + Delete a file from disk. + + int DeleteFile Returns 0 if OK or something else in case of an error. + + char *filename The name of the file to delete. + ++++++++++++++++++++++++++++++++++++++*/ + +int DeleteFile(char *filename) +{ + unlink(filename); + + return(0); +} diff --git a/src/functions.h b/src/functions.h new file mode 100644 index 0000000..1ff10de --- /dev/null +++ b/src/functions.h @@ -0,0 +1,106 @@ +/*************************************** + $Header: /home/amb/routino/src/RCS/functions.h,v 1.54 2010/04/24 16:47:56 amb Exp $ + + Header file for function prototypes + + Part of the Routino routing software. + ******************/ /****************** + This file Copyright 2008-2010 Andrew M. Bishop + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + ***************************************/ + + +#ifndef FUNCTIONS_H +#define FUNCTIONS_H /*+ To stop multiple inclusions. +*/ + +#include +#include + +#include "types.h" +#include "profiles.h" +#include "results.h" + + +/* In router.c */ + +/*+ Return true if this is a fake node. +*/ +#define IsFakeNode(xxx) ((xxx)&NODE_SUPER) + +index_t CreateFakes(Nodes *nodes,int point,Segment *segment,index_t node1,index_t node2,distance_t dist1,distance_t dist2); + +void GetFakeLatLong(index_t node, double *latitude,double *longitude); + +Segment *FirstFakeSegment(index_t node); +Segment *NextFakeSegment(Segment *segment,index_t node); +Segment *ExtraFakeSegment(index_t node,index_t fakenode); + + +/* In files.c */ + +char *FileName(const char *dirname,const char *prefix, const char *name); + +void *MapFile(const char *filename); +void *UnmapFile(const char *filename); + +int OpenFile(const char *filename); +int AppendFile(const char *filename); +int ReOpenFile(const char *filename); + +int WriteFile(int fd,const void *address,size_t length); +int ReadFile(int fd,void *address,size_t length); + +off_t SizeFile(const char *filename); +int ExistsFile(const char *filename); + +int SeekFile(int fd,off_t position); + +void CloseFile(int fd); + +int DeleteFile(char *filename); + + +/* In optimiser.c */ + +Results *FindNormalRoute(Nodes *nodes,Segments *segments,Ways *ways,index_t start,index_t finish,Profile *profile); +Results *FindMiddleRoute(Nodes *supernodes,Segments *supersegments,Ways *superways,Results *begin,Results *end,Profile *profile); + +Results *FindStartRoutes(Nodes *nodes,Segments *segments,Ways *ways,index_t start,Profile *profile); +Results *FindFinishRoutes(Nodes *nodes,Segments *segments,Ways *ways,index_t finish,Profile *profile); + +Results *CombineRoutes(Results *results,Nodes *nodes,Segments *segments,Ways *ways,Profile *profile); + +void FixForwardRoute(Results *results,index_t finish); + + +/* In output.c */ + +void PrintRoute(Results **results,int nresults,Nodes *nodes,Segments *segments,Ways *ways,Profile *profile); + + +/* In sorting.c */ + +/*+ The type, size and alignment of variable to store the variable length +*/ +#define FILESORT_VARINT unsigned short +#define FILESORT_VARSIZE sizeof(FILESORT_VARINT) +#define FILESORT_VARALIGN sizeof(void*) + +void filesort_fixed(int fd_in,int fd_out,size_t itemsize,int (*compare)(const void*,const void*),int (*buildindex)(void*,index_t)); + +void filesort_vary(int fd_in,int fd_out,int (*compare)(const void*,const void*),int (*buildindex)(void*,index_t)); + +void heapsort(void **datap,size_t nitems,int(*compare)(const void*, const void*)); + + +#endif /* FUNCTIONS_H */ diff --git a/src/functionsx.h b/src/functionsx.h new file mode 100644 index 0000000..bb92183 --- /dev/null +++ b/src/functionsx.h @@ -0,0 +1,39 @@ +/*************************************** + $Header: /home/amb/routino/src/RCS/functionsx.h,v 1.5 2010/05/22 18:40:47 amb Exp $ + + Header file for function prototypes for extended data types. + + Part of the Routino routing software. + ******************/ /****************** + This file Copyright 2008-2010 Andrew M. Bishop + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + ***************************************/ + + +#ifndef FUNCTIONSX_H +#define FUNCTIONSX_H /*+ To stop multiple inclusions. +*/ + +#include + +#include "typesx.h" +#include "profiles.h" + + +/* In osmparser.c */ + +int ParseOSM(FILE *file,NodesX *OSMNodes,SegmentsX *OSMSegments,WaysX *OSMWays); + + +#endif /* FUNCTIONSX_H */ diff --git a/src/nodes.c b/src/nodes.c new file mode 100644 index 0000000..7430d62 --- /dev/null +++ b/src/nodes.c @@ -0,0 +1,498 @@ +/*************************************** + $Header: /home/amb/routino/src/RCS/nodes.c,v 1.39 2010/07/08 17:54:54 amb Exp $ + + Node data type functions. + + Part of the Routino routing software. + ******************/ /****************** + This file Copyright 2008-2010 Andrew M. Bishop + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + ***************************************/ + + +#include +#include +#include + +#include "profiles.h" +#include "nodes.h" +#include "segments.h" +#include "ways.h" +#include "functions.h" + + +/*++++++++++++++++++++++++++++++++++++++ + Load in a node list from a file. + + Nodes* LoadNodeList Returns the node list. + + const char *filename The name of the file to load. + ++++++++++++++++++++++++++++++++++++++*/ + +Nodes *LoadNodeList(const char *filename) +{ + void *data; + Nodes *nodes; + + nodes=(Nodes*)malloc(sizeof(Nodes)); + + data=MapFile(filename); + + /* Copy the Nodes structure from the loaded data */ + + *nodes=*((Nodes*)data); + + /* Adjust the pointers in the Nodes structure. */ + + nodes->data=data; + nodes->offsets=(index_t*)(data+sizeof(Nodes)); + nodes->nodes=(Node*)(data+(sizeof(Nodes)+(nodes->latbins*nodes->lonbins+1)*sizeof(index_t))); + + return(nodes); +} + + +/*++++++++++++++++++++++++++++++++++++++ + Find the closest node given its latitude, longitude and optionally profile. + + index_t FindClosestNode Returns the closest node. + + Nodes* nodes The set of nodes to search. + + Segments *segments The set of segments to use. + + Ways *ways The set of ways to use. + + double latitude The latitude to look for. + + double longitude The longitude to look for. + + distance_t distance The maximum distance to look. + + Profile *profile The profile of the mode of transport (or NULL). + + distance_t *bestdist Returns the distance to the best node. + ++++++++++++++++++++++++++++++++++++++*/ + +index_t FindClosestNode(Nodes* nodes,Segments *segments,Ways *ways,double latitude,double longitude, + distance_t distance,Profile *profile,distance_t *bestdist) +{ + ll_bin_t latbin=latlong_to_bin(radians_to_latlong(latitude ))-nodes->latzero; + ll_bin_t lonbin=latlong_to_bin(radians_to_latlong(longitude))-nodes->lonzero; + int delta=0,count; + index_t i,bestn=NO_NODE; + distance_t bestd=INF_DISTANCE; + + /* Start with the bin containing the location, then spiral outwards. */ + + do + { + int latb,lonb,llbin; + + count=0; + + for(latb=latbin-delta;latb<=latbin+delta;latb++) + { + if(latb<0 || latb>=nodes->latbins) + continue; + + for(lonb=lonbin-delta;lonb<=lonbin+delta;lonb++) + { + if(lonb<0 || lonb>=nodes->lonbins) + continue; + + if(abs(latb-latbin)latbins+latb; + + /* Check if this grid square has any hope of being close enough */ + + if(delta>0) + { + double lat1=latlong_to_radians(bin_to_latlong(nodes->latzero+latb)); + double lon1=latlong_to_radians(bin_to_latlong(nodes->lonzero+lonb)); + double lat2=latlong_to_radians(bin_to_latlong(nodes->latzero+latb+1)); + double lon2=latlong_to_radians(bin_to_latlong(nodes->lonzero+lonb+1)); + + if(latb==latbin) + { + distance_t dist1=Distance(latitude,lon1,latitude,longitude); + distance_t dist2=Distance(latitude,lon2,latitude,longitude); + + if(dist1>distance && dist2>distance) + continue; + } + else if(lonb==lonbin) + { + distance_t dist1=Distance(lat1,longitude,latitude,longitude); + distance_t dist2=Distance(lat2,longitude,latitude,longitude); + + if(dist1>distance && dist2>distance) + continue; + } + else + { + distance_t dist1=Distance(lat1,lon1,latitude,longitude); + distance_t dist2=Distance(lat2,lon1,latitude,longitude); + distance_t dist3=Distance(lat2,lon2,latitude,longitude); + distance_t dist4=Distance(lat1,lon2,latitude,longitude); + + if(dist1>distance && dist2>distance && dist3>distance && dist4>distance) + continue; + } + } + + /* Check every node in this grid square. */ + + for(i=nodes->offsets[llbin];ioffsets[llbin+1];i++) + { + double lat=latlong_to_radians(bin_to_latlong(nodes->latzero+latb)+off_to_latlong(nodes->nodes[i].latoffset)); + double lon=latlong_to_radians(bin_to_latlong(nodes->lonzero+lonb)+off_to_latlong(nodes->nodes[i].lonoffset)); + + distance_t dist=Distance(lat,lon,latitude,longitude); + + if(distway); + + if(way->allow&profile->allow) + break; + + segment=NextSegment(segments,segment,i); + } + while(segment); + + if(!segment) + continue; + } + + bestn=i; bestd=distance=dist; + } + } + + count++; + } + } + + delta++; + } + while(count); + + *bestdist=bestd; + + return(bestn); +} + + +/*++++++++++++++++++++++++++++++++++++++ + Find the closest segment to a latitude, longitude and optionally profile. + + Segment *FindClosestSegment Returns the closest segment. + + Nodes* nodes The set of nodes to search. + + Segments *segments The set of segments to use. + + Ways *ways The set of ways to use. + + double latitude The latitude to look for. + + double longitude The longitude to look for. + + distance_t distance The maximum distance to look. + + Profile *profile The profile of the mode of transport (or NULL). + + distance_t *bestdist Returns the distance to the best segment. + + index_t *bestnode1 Returns the best node at one end. + + index_t *bestnode2 Returns the best node at the other end. + + distance_t *bestdist1 Returns the distance to the best node at one end. + + distance_t *bestdist2 Returns the distance to the best node at the other end. + ++++++++++++++++++++++++++++++++++++++*/ + +Segment *FindClosestSegment(Nodes* nodes,Segments *segments,Ways *ways,double latitude,double longitude, + distance_t distance,Profile *profile, distance_t *bestdist, + index_t *bestnode1,index_t *bestnode2,distance_t *bestdist1,distance_t *bestdist2) +{ + ll_bin_t latbin=latlong_to_bin(radians_to_latlong(latitude ))-nodes->latzero; + ll_bin_t lonbin=latlong_to_bin(radians_to_latlong(longitude))-nodes->lonzero; + int delta=0,count; + index_t i,bestn1=NO_NODE,bestn2=NO_NODE; + distance_t bestd=INF_DISTANCE,bestd1=INF_DISTANCE,bestd2=INF_DISTANCE; + Segment *bests=NULL; + + /* Start with the bin containing the location, then spiral outwards. */ + + do + { + int latb,lonb,llbin; + + count=0; + + for(latb=latbin-delta;latb<=latbin+delta;latb++) + { + if(latb<0 || latb>=nodes->latbins) + continue; + + for(lonb=lonbin-delta;lonb<=lonbin+delta;lonb++) + { + if(lonb<0 || lonb>=nodes->lonbins) + continue; + + if(abs(latb-latbin)latbins+latb; + + /* Check if this grid square has any hope of being close enough */ + + if(delta>0) + { + double lat1=latlong_to_radians(bin_to_latlong(nodes->latzero+latb)); + double lon1=latlong_to_radians(bin_to_latlong(nodes->lonzero+lonb)); + double lat2=latlong_to_radians(bin_to_latlong(nodes->latzero+latb+1)); + double lon2=latlong_to_radians(bin_to_latlong(nodes->lonzero+lonb+1)); + + if(latb==latbin) + { + distance_t dist1=Distance(latitude,lon1,latitude,longitude); + distance_t dist2=Distance(latitude,lon2,latitude,longitude); + + if(dist1>distance && dist2>distance) + continue; + } + else if(lonb==lonbin) + { + distance_t dist1=Distance(lat1,longitude,latitude,longitude); + distance_t dist2=Distance(lat2,longitude,latitude,longitude); + + if(dist1>distance && dist2>distance) + continue; + } + else + { + distance_t dist1=Distance(lat1,lon1,latitude,longitude); + distance_t dist2=Distance(lat2,lon1,latitude,longitude); + distance_t dist3=Distance(lat2,lon2,latitude,longitude); + distance_t dist4=Distance(lat1,lon2,latitude,longitude); + + if(dist1>distance && dist2>distance && dist3>distance && dist4>distance) + continue; + } + } + + /* Check every node in this grid square. */ + + for(i=nodes->offsets[llbin];ioffsets[llbin+1];i++) + { + double lat1=latlong_to_radians(bin_to_latlong(nodes->latzero+latb)+off_to_latlong(nodes->nodes[i].latoffset)); + double lon1=latlong_to_radians(bin_to_latlong(nodes->lonzero+lonb)+off_to_latlong(nodes->nodes[i].lonoffset)); + distance_t dist1; + + dist1=Distance(lat1,lon1,latitude,longitude); + + if(dist1way); + + if(!profile || way->allow&profile->allow) + { + distance_t dist2,dist3; + double lat2,lon2,dist3a,dist3b,distp; + + GetLatLong(nodes,OtherNode(segment,i),&lat2,&lon2); + + dist2=Distance(lat2,lon2,latitude,longitude); + + dist3=Distance(lat1,lon1,lat2,lon2); + + /* Use law of cosines (assume flat Earth) */ + + dist3a=((double)dist1*(double)dist1-(double)dist2*(double)dist2+(double)dist3*(double)dist3)/(2.0*(double)dist3); + dist3b=(double)dist3-dist3a; + + if(dist3a>=0 && dist3b>=0) + distp=sqrt((double)dist1*(double)dist1-dist3a*dist3a); + else if(dist3a>0) + { + distp=dist2; + dist3a=dist3; + dist3b=0; + } + else /* if(dist3b>0) */ + { + distp=dist1; + dist3a=0; + dist3b=dist3; + } + + if(distp<(double)bestd) + { + bests=segment; + bestn1=i; + bestn2=OtherNode(segment,i); + bestd1=(distance_t)dist3a; + bestd2=(distance_t)dist3b; + bestd=(distance_t)distp; + } + } + } + + segment=NextSegment(segments,segment,i); + } + while(segment); + } + + } /* dist1 < distance */ + + count++; + } + } + + delta++; + } + while(count); + + *bestdist=bestd; + + *bestnode1=bestn1; + *bestnode2=bestn2; + *bestdist1=bestd1; + *bestdist2=bestd2; + + return(bests); +} + + +/*++++++++++++++++++++++++++++++++++++++ + Get the latitude and longitude associated with a node. + + Nodes *nodes The set of nodes. + + index_t index The node index. + + double *latitude Returns the latitude. + + double *longitude Returns the logitude. + ++++++++++++++++++++++++++++++++++++++*/ + +void GetLatLong(Nodes *nodes,index_t index,double *latitude,double *longitude) +{ + Node *node=&nodes->nodes[index]; + int latbin=-1,lonbin=-1; + int start,end,mid; + + /* Binary search - search key closest below is required. + * + * # <- start | Check mid and move start or end if it doesn't match + * # | + * # | Since an inexact match is wanted we must set end=mid-1 + * # <- mid | or start=mid because we know that mid doesn't match. + * # | + * # | Eventually either end=start or end=start+1 and one of + * # <- end | start or end is the wanted one. + */ + + /* Search for longitude */ + + start=0; + end=nodes->lonbins-1; + + do + { + mid=(start+end)/2; /* Choose mid point */ + + if(nodes->offsets[nodes->latbins*mid]offsets[nodes->latbins*mid]>index) /* Mid point is too high */ + end=mid-1; + else /* Mid point is correct */ + {lonbin=mid;break;} + } + while((end-start)>1); + + if(lonbin==-1) + { + if(nodes->offsets[nodes->latbins*end]>index) + lonbin=start; + else + lonbin=end; + } + + while(lonbinlonbins && nodes->offsets[lonbin*nodes->latbins]==nodes->offsets[(lonbin+1)*nodes->latbins]) + lonbin++; + + /* Search for latitude */ + + start=0; + end=nodes->latbins-1; + + do + { + mid=(start+end)/2; /* Choose mid point */ + + if(nodes->offsets[lonbin*nodes->latbins+mid]offsets[lonbin*nodes->latbins+mid]>index) /* Mid point is too high */ + end=mid-1; + else /* Mid point is correct */ + {latbin=mid;break;} + } + while((end-start)>1); + + if(latbin==-1) + { + if(nodes->offsets[lonbin*nodes->latbins+end]>index) + latbin=start; + else + latbin=end; + } + + while(latbinlatbins && nodes->offsets[lonbin*nodes->latbins+latbin]==nodes->offsets[lonbin*nodes->latbins+latbin+1]) + latbin++; + + /* Return the values */ + + *latitude =latlong_to_radians(bin_to_latlong(nodes->latzero+latbin)+off_to_latlong(node->latoffset)); + *longitude=latlong_to_radians(bin_to_latlong(nodes->lonzero+lonbin)+off_to_latlong(node->lonoffset)); +} diff --git a/src/nodes.h b/src/nodes.h new file mode 100644 index 0000000..8bfc2b6 --- /dev/null +++ b/src/nodes.h @@ -0,0 +1,94 @@ +/*************************************** + $Header: /home/amb/routino/src/RCS/nodes.h,v 1.30 2009/11/14 19:39:19 amb Exp $ + + A header file for the nodes. + + Part of the Routino routing software. + ******************/ /****************** + This file Copyright 2008,2009 Andrew M. Bishop + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + ***************************************/ + + +#ifndef NODES_H +#define NODES_H /*+ To stop multiple inclusions. +*/ + +#include + +#include "types.h" +#include "profiles.h" + + +/* Data structures */ + + +/*+ A structure containing a single node. +*/ +struct _Node +{ + index_t firstseg; /*+ The index of the first segment. +*/ + + ll_off_t latoffset; /*+ The node latitude offset within its bin. +*/ + ll_off_t lonoffset; /*+ The node longitude offset within its bin. +*/ +}; + + +/*+ A structure containing a set of nodes (mmap format). +*/ +struct _Nodes +{ + uint32_t number; /*+ How many nodes in total? +*/ + uint32_t snumber; /*+ How many super-nodes? +*/ + + uint32_t latbins; /*+ The number of bins containing latitude. +*/ + uint32_t lonbins; /*+ The number of bins containing longitude. +*/ + + ll_bin_t latzero; /*+ The bin number of the furthest south bin. +*/ + ll_bin_t lonzero; /*+ The bin number of the furthest west bin. +*/ + + index_t *offsets; /*+ An array of offset to the first node in each bin. +*/ + + Node *nodes; /*+ An array of nodes. +*/ + + void *data; /*+ The memory mapped data. +*/ +}; + + +/* Macros */ + +/*+ Return a Node pointer given a set of nodes and an index. +*/ +#define LookupNode(xxx,yyy) (&(xxx)->nodes[yyy]) + +/*+ Return a Segment points given a Node pointer and a set of segments. +*/ +#define FirstSegment(xxx,yyy,zzz) LookupSegment((xxx),SEGMENT((yyy)->nodes[zzz].firstseg)) + +/*+ Return true if this is a super-node. +*/ +#define IsSuperNode(xxx,yyy) (((xxx)->nodes[yyy].firstseg)&NODE_SUPER) + + +/* Functions */ + + +Nodes *LoadNodeList(const char *filename); + +index_t FindClosestNode(Nodes* nodes,Segments *segments,Ways *ways,double latitude,double longitude, + distance_t distance,Profile *profile,distance_t *bestdist); + +Segment *FindClosestSegment(Nodes* nodes,Segments *segments,Ways *ways,double latitude,double longitude, + distance_t distance,Profile *profile, distance_t *bestdist, + index_t *bestnode1,index_t *bestnode2,distance_t *bestdist1,distance_t *bestdist2); + +void GetLatLong(Nodes *nodes,index_t index,double *latitude,double *longitude); + + +#endif /* NODES_H */ diff --git a/src/nodesx.c b/src/nodesx.c new file mode 100644 index 0000000..cc1e0a2 --- /dev/null +++ b/src/nodesx.c @@ -0,0 +1,894 @@ +/*************************************** + $Header: /home/amb/routino/src/RCS/nodesx.c,v 1.56 2010/04/28 17:27:02 amb Exp $ + + Extented Node data type functions. + + Part of the Routino routing software. + ******************/ /****************** + This file Copyright 2008-2010 Andrew M. Bishop + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + ***************************************/ + + +#include +#include +#include +#include +#include + +#include "types.h" +#include "functions.h" +#include "nodesx.h" +#include "segmentsx.h" +#include "waysx.h" +#include "segments.h" +#include "nodes.h" + + +/* Variables */ + +/*+ The command line '--slim' option. +*/ +extern int option_slim; + +/*+ The command line '--tmpdir' option or its default value. +*/ +extern char *option_tmpdirname; + +/*+ A temporary file-local variable for use by the sort functions. +*/ +static NodesX *sortnodesx; + +/* Functions */ + +static int sort_by_id(NodeX *a,NodeX *b); +static int index_by_id(NodeX *nodex,index_t index); + +static int sort_by_lat_long(NodeX *a,NodeX *b); +static int index_by_lat_long(NodeX *nodex,index_t index); + + +/*++++++++++++++++++++++++++++++++++++++ + Allocate a new node list (create a new file or open an existing one). + + NodesX *NewNodeList Returns the node list. + + int append Set to 1 if the file is to be opened for appending (now or later). + ++++++++++++++++++++++++++++++++++++++*/ + +NodesX *NewNodeList(int append) +{ + NodesX *nodesx; + + nodesx=(NodesX*)calloc(1,sizeof(NodesX)); + + assert(nodesx); /* Check calloc() worked */ + + nodesx->filename=(char*)malloc(strlen(option_tmpdirname)+32); + + if(append) + sprintf(nodesx->filename,"%s/nodes.input.tmp",option_tmpdirname); + else + sprintf(nodesx->filename,"%s/nodes.%p.tmp",option_tmpdirname,nodesx); + + if(append) + { + off_t size; + + nodesx->fd=AppendFile(nodesx->filename); + + size=SizeFile(nodesx->filename); + + nodesx->xnumber=size/sizeof(NodeX); + } + else + nodesx->fd=OpenFile(nodesx->filename); + + return(nodesx); +} + + +/*++++++++++++++++++++++++++++++++++++++ + Free a node list. + + NodesX *nodesx The list to be freed. + + int keep Set to 1 if the file is to be kept. + ++++++++++++++++++++++++++++++++++++++*/ + +void FreeNodeList(NodesX *nodesx,int keep) +{ + if(!keep) + DeleteFile(nodesx->filename); + + free(nodesx->filename); + + if(nodesx->idata) + free(nodesx->idata); + + if(nodesx->ndata) + free(nodesx->ndata); + + if(nodesx->super) + free(nodesx->super); + + if(nodesx->offsets) + free(nodesx->offsets); + + free(nodesx); +} + + +/*++++++++++++++++++++++++++++++++++++++ + Append a node to a newly created node list (unsorted). + + NodesX* nodesx The set of nodes to process. + + node_t id The node identification. + + double latitude The latitude of the node. + + double longitude The longitude of the node. + ++++++++++++++++++++++++++++++++++++++*/ + +void AppendNode(NodesX* nodesx,node_t id,double latitude,double longitude) +{ + NodeX nodex; + + assert(!nodesx->idata); /* Must not have idata filled in => unsorted */ + + nodex.id=id; + nodex.latitude =radians_to_latlong(latitude); + nodex.longitude=radians_to_latlong(longitude); + + WriteFile(nodesx->fd,&nodex,sizeof(NodeX)); + + nodesx->xnumber++; +} + + +/*++++++++++++++++++++++++++++++++++++++ + Sort the node list (i.e. create the sortable indexes). + + NodesX* nodesx The set of nodes to process. + ++++++++++++++++++++++++++++++++++++++*/ + +void SortNodeList(NodesX* nodesx) +{ + int fd; + + /* Check the start conditions */ + + assert(!nodesx->idata); /* Must not have idata filled in => unsorted */ + + /* Print the start message */ + + printf("Sorting Nodes"); + fflush(stdout); + + /* Close the files and re-open them (finished appending) */ + + CloseFile(nodesx->fd); + nodesx->fd=ReOpenFile(nodesx->filename); + + DeleteFile(nodesx->filename); + + fd=OpenFile(nodesx->filename); + + /* Allocate the array of indexes */ + + nodesx->idata=(node_t*)malloc(nodesx->xnumber*sizeof(node_t)); + + assert(nodesx->idata); /* Check malloc() worked */ + + /* Sort by node indexes */ + + sortnodesx=nodesx; + + filesort_fixed(nodesx->fd,fd,sizeof(NodeX),(int (*)(const void*,const void*))sort_by_id,(int (*)(void*,index_t))index_by_id); + + /* Close the files and re-open them */ + + CloseFile(nodesx->fd); + CloseFile(fd); + + nodesx->fd=ReOpenFile(nodesx->filename); + + /* Print the final message */ + + printf("\rSorted Nodes: Nodes=%d Duplicates=%d\n",nodesx->xnumber,nodesx->xnumber-nodesx->number); + fflush(stdout); +} + + +/*++++++++++++++++++++++++++++++++++++++ + Sort the nodes into id order. + + int sort_by_id Returns the comparison of the id fields. + + NodeX *a The first extended node. + + NodeX *b The second extended node. + ++++++++++++++++++++++++++++++++++++++*/ + +static int sort_by_id(NodeX *a,NodeX *b) +{ + node_t a_id=a->id; + node_t b_id=b->id; + + if(a_idb_id) + return(1); + else + return(0); +} + + +/*++++++++++++++++++++++++++++++++++++++ + Index the nodes after sorting. + + int index_by_id Return 1 if the value is to be kept, otherwise zero. + + NodeX *nodex The extended node. + + index_t index The index of this node in the total. + ++++++++++++++++++++++++++++++++++++++*/ + +static int index_by_id(NodeX *nodex,index_t index) +{ + if(index==0 || sortnodesx->idata[index-1]!=nodex->id) + { + sortnodesx->idata[index]=nodex->id; + + sortnodesx->number++; + + return(1); + } + + return(0); +} + + +/*++++++++++++++++++++++++++++++++++++++ + Sort the node list geographically. + + NodesX* nodesx The set of nodes to process. + ++++++++++++++++++++++++++++++++++++++*/ + +void SortNodeListGeographically(NodesX* nodesx) +{ + int fd; + + /* Print the start message */ + + printf("Sorting Nodes Geographically"); + fflush(stdout); + + /* Allocate the memory for the geographical offsets array */ + + nodesx->offsets=(index_t*)malloc((nodesx->latbins*nodesx->lonbins+1)*sizeof(index_t)); + + nodesx->latlonbin=0; + + /* Close the files and re-open them */ + + CloseFile(nodesx->fd); + nodesx->fd=ReOpenFile(nodesx->filename); + + DeleteFile(nodesx->filename); + + fd=OpenFile(nodesx->filename); + + /* Sort geographically */ + + sortnodesx=nodesx; + + filesort_fixed(nodesx->fd,fd,sizeof(NodeX),(int (*)(const void*,const void*))sort_by_lat_long,(int (*)(void*,index_t))index_by_lat_long); + + /* Close the files and re-open them */ + + CloseFile(nodesx->fd); + CloseFile(fd); + + nodesx->fd=ReOpenFile(nodesx->filename); + + /* Finish off the indexing */ + + for(;nodesx->latlonbin<=(nodesx->latbins*nodesx->lonbins);nodesx->latlonbin++) + nodesx->offsets[nodesx->latlonbin]=nodesx->number; + + /* Print the final message */ + + printf("\rSorted Nodes Geographically \n"); + fflush(stdout); +} + + +/*++++++++++++++++++++++++++++++++++++++ + Sort the nodes into latitude and longitude order. + + int sort_by_lat_long Returns the comparison of the latitude and longitude fields. + + NodeX *a The first extended node. + + NodeX *b The second extended node. + ++++++++++++++++++++++++++++++++++++++*/ + +static int sort_by_lat_long(NodeX *a,NodeX *b) +{ + ll_bin_t a_lon=latlong_to_bin(a->longitude); + ll_bin_t b_lon=latlong_to_bin(b->longitude); + + if(a_lonb_lon) + return(1); + else + { + ll_bin_t a_lat=latlong_to_bin(a->latitude); + ll_bin_t b_lat=latlong_to_bin(b->latitude); + + if(a_latb_lat) + return(1); + else + { +#ifdef REGRESSION_TESTING + // Need this for regression testing because heapsort() is not order + // preserving like qsort() is (or was when tested). + + index_t a_id=a->id; + index_t b_id=b->id; + + if(a_idb_id) + return(1); + else +#endif + return(0); + } + } +} + + +/*++++++++++++++++++++++++++++++++++++++ + Index the nodes after sorting. + + int index_by_lat_long Return 1 if the value is to be kept, otherwise zero. + + NodeX *nodex The extended node. + + index_t index The index of this node in the total. + ++++++++++++++++++++++++++++++++++++++*/ + +static int index_by_lat_long(NodeX *nodex,index_t index) +{ + /* Work out the offsets */ + + ll_bin_t latbin=latlong_to_bin(nodex->latitude )-sortnodesx->latzero; + ll_bin_t lonbin=latlong_to_bin(nodex->longitude)-sortnodesx->lonzero; + int llbin=lonbin*sortnodesx->latbins+latbin; + + for(;sortnodesx->latlonbin<=llbin;sortnodesx->latlonbin++) + sortnodesx->offsets[sortnodesx->latlonbin]=index; + + return(1); +} + + +/*++++++++++++++++++++++++++++++++++++++ + Find a particular node index. + + index_t IndexNodeX Returns the index of the extended node with the specified id. + + NodesX* nodesx The set of nodes to process. + + node_t id The node id to look for. + ++++++++++++++++++++++++++++++++++++++*/ + +index_t IndexNodeX(NodesX* nodesx,node_t id) +{ + int start=0; + int end=nodesx->number-1; + int mid; + + assert(nodesx->idata); /* Must have idata filled in => sorted by id */ + + /* Binary search - search key exact match only is required. + * + * # <- start | Check mid and move start or end if it doesn't match + * # | + * # | Since an exact match is wanted we can set end=mid-1 + * # <- mid | or start=mid+1 because we know that mid doesn't match. + * # | + * # | Eventually either end=start or end=start+1 and one of + * # <- end | start or end is the wanted one. + */ + + if(endidata[start]) /* Check key is not before start */ + return(NO_NODE); + else if(id>nodesx->idata[end]) /* Check key is not after end */ + return(NO_NODE); + else + { + do + { + mid=(start+end)/2; /* Choose mid point */ + + if(nodesx->idata[mid]idata[mid]>id) /* Mid point is too high */ + end=mid-1; + else /* Mid point is correct */ + return(mid); + } + while((end-start)>1); + + if(nodesx->idata[start]==id) /* Start is correct */ + return(start); + + if(nodesx->idata[end]==id) /* End is correct */ + return(end); + } + + return(NO_NODE); +} + + +/*++++++++++++++++++++++++++++++++++++++ + Lookup a particular node. + + NodeX *LookupNodeX Returns a pointer to the extended node with the specified id. + + NodesX* nodesx The set of nodes to process. + + index_t index The node index to look for. + + int position The position in the cache to use. + ++++++++++++++++++++++++++++++++++++++*/ + +NodeX *LookupNodeX(NodesX* nodesx,index_t index,int position) +{ + assert(index!=NO_NODE); /* Must be a valid node */ + + if(option_slim) + { + SeekFile(nodesx->fd,index*sizeof(NodeX)); + + ReadFile(nodesx->fd,&nodesx->cached[position-1],sizeof(NodeX)); + + return(&nodesx->cached[position-1]); + } + else + { + return(&nodesx->xdata[index]); + } +} + + +/*++++++++++++++++++++++++++++++++++++++ + Remove any nodes that are not part of a highway. + + NodesX *nodesx The complete node list. + + SegmentsX *segmentsx The list of segments. + ++++++++++++++++++++++++++++++++++++++*/ + +void RemoveNonHighwayNodes(NodesX *nodesx,SegmentsX *segmentsx) +{ + NodeX nodex; + int total=0,highway=0,nothighway=0; + ll_bin_t lat_min_bin,lat_max_bin,lon_min_bin,lon_max_bin; + latlong_t lat_min,lat_max,lon_min,lon_max; + int fd; + + /* Check the start conditions */ + + assert(nodesx->idata); /* Must have idata filled in => data sorted */ + + /* Print the start message */ + + printf("Checking: Nodes=0"); + fflush(stdout); + + /* While we are here we can work out the range of data */ + + lat_min=radians_to_latlong( 2); + lat_max=radians_to_latlong(-2); + lon_min=radians_to_latlong( 4); + lon_max=radians_to_latlong(-4); + + /* Modify the on-disk image */ + + DeleteFile(nodesx->filename); + + fd=OpenFile(nodesx->filename); + SeekFile(nodesx->fd,0); + + while(!ReadFile(nodesx->fd,&nodex,sizeof(NodeX))) + { + if(IndexFirstSegmentX(segmentsx,nodex.id)==NO_SEGMENT) + nothighway++; + else + { + nodex.id=highway; + + WriteFile(fd,&nodex,sizeof(NodeX)); + + nodesx->idata[highway]=nodesx->idata[total]; + highway++; + + if(nodex.latitudelat_max) + lat_max=nodex.latitude; + if(nodex.longitudelon_max) + lon_max=nodex.longitude; + } + + total++; + + if(!(total%10000)) + { + printf("\rChecking: Nodes=%d Highway=%d not-Highway=%d",total,highway,nothighway); + fflush(stdout); + } + } + + /* Close the files and re-open them */ + + CloseFile(nodesx->fd); + CloseFile(fd); + + nodesx->fd=ReOpenFile(nodesx->filename); + + nodesx->number=highway; + + /* Work out the number of bins */ + + lat_min_bin=latlong_to_bin(lat_min); + lon_min_bin=latlong_to_bin(lon_min); + lat_max_bin=latlong_to_bin(lat_max); + lon_max_bin=latlong_to_bin(lon_max); + + nodesx->latzero=lat_min_bin; + nodesx->lonzero=lon_min_bin; + + nodesx->latbins=(lat_max_bin-lat_min_bin)+1; + nodesx->lonbins=(lon_max_bin-lon_min_bin)+1; + + /* Allocate and clear the super-node markers */ + + nodesx->super=(uint8_t*)calloc(nodesx->number,sizeof(uint8_t)); + + assert(nodesx->super); /* Check calloc() worked */ + + /* Print the final message */ + + printf("\rChecked: Nodes=%d Highway=%d not-Highway=%d \n",total,highway,nothighway); + fflush(stdout); +} + + +/*++++++++++++++++++++++++++++++++++++++ + Create the real node data. + + NodesX *nodesx The set of nodes to use. + + int iteration The final super-node iteration. + ++++++++++++++++++++++++++++++++++++++*/ + +void CreateRealNodes(NodesX *nodesx,int iteration) +{ + index_t i; + + /* Check the start conditions */ + + assert(!nodesx->ndata); /* Must not have ndata filled in => no real nodes */ + + /* Print the start message */ + + printf("Creating Real Nodes: Nodes=0"); + fflush(stdout); + + /* Map into memory */ + + if(!option_slim) + nodesx->xdata=MapFile(nodesx->filename); + + /* Allocate the memory */ + + nodesx->ndata=(Node*)malloc(nodesx->number*sizeof(Node)); + + assert(nodesx->ndata); /* Check malloc() worked */ + + /* Loop through and allocate. */ + + for(i=0;inumber;i++) + { + NodeX *nodex=LookupNodeX(nodesx,i,1); + + nodesx->ndata[nodex->id].latoffset=latlong_to_off(nodex->latitude); + nodesx->ndata[nodex->id].lonoffset=latlong_to_off(nodex->longitude); + nodesx->ndata[nodex->id].firstseg=SEGMENT(NO_SEGMENT); + + if(nodesx->super[nodex->id]==iteration) + nodesx->ndata[nodex->id].firstseg|=NODE_SUPER; + + if(!((i+1)%10000)) + { + printf("\rCreating Real Nodes: Nodes=%d",i+1); + fflush(stdout); + } + } + + /* Free the unneeded memory */ + + free(nodesx->super); + nodesx->super=NULL; + + /* Unmap from memory */ + + if(!option_slim) + nodesx->xdata=UnmapFile(nodesx->filename); + + /* Print the final message */ + + printf("\rCreating Real Nodes: Nodes=%d \n",nodesx->number); + fflush(stdout); +} + + +/*++++++++++++++++++++++++++++++++++++++ + Assign the segment indexes to the nodes. + + NodesX *nodesx The list of nodes to process. + + SegmentsX* segmentsx The set of segments to use. + ++++++++++++++++++++++++++++++++++++++*/ + +void IndexNodes(NodesX *nodesx,SegmentsX *segmentsx) +{ + index_t i; + + /* Check the start conditions */ + + assert(nodesx->ndata); /* Must have ndata filled in => real nodes exist */ + assert(segmentsx->sdata); /* Must have sdata filled in => real segments exist */ + + /* Print the start message */ + + printf("Indexing Segments: Segments=0"); + fflush(stdout); + + /* Map into memory */ + + if(!option_slim) + { + nodesx->xdata=MapFile(nodesx->filename); + segmentsx->xdata=MapFile(segmentsx->filename); + } + + /* Index the nodes */ + + for(i=0;inumber;i++) + { + SegmentX *segmentx=LookupSegmentX(segmentsx,i,1); + node_t id1=segmentx->node1; + node_t id2=segmentx->node2; + Node *node1=&nodesx->ndata[id1]; + Node *node2=&nodesx->ndata[id2]; + + /* Check node1 */ + + if(SEGMENT(node1->firstseg)==SEGMENT(NO_SEGMENT)) + { + node1->firstseg^=SEGMENT(NO_SEGMENT); + node1->firstseg|=i; + } + else + { + index_t index=SEGMENT(node1->firstseg); + + do + { + segmentx=LookupSegmentX(segmentsx,index,1); + + if(segmentx->node1==id1) + { + index++; + + if(index>=segmentsx->number) + break; + + segmentx=LookupSegmentX(segmentsx,index,1); + + if(segmentx->node1!=id1) + break; + } + else + { + if(segmentsx->sdata[index].next2==NO_NODE) + { + segmentsx->sdata[index].next2=i; + break; + } + else + index=segmentsx->sdata[index].next2; + } + } + while(1); + } + + /* Check node2 */ + + if(SEGMENT(node2->firstseg)==SEGMENT(NO_SEGMENT)) + { + node2->firstseg^=SEGMENT(NO_SEGMENT); + node2->firstseg|=i; + } + else + { + index_t index=SEGMENT(node2->firstseg); + + do + { + segmentx=LookupSegmentX(segmentsx,index,1); + + if(segmentx->node1==id2) + { + index++; + + if(index>=segmentsx->number) + break; + + segmentx=LookupSegmentX(segmentsx,index,1); + + if(segmentx->node1!=id2) + break; + } + else + { + if(segmentsx->sdata[index].next2==NO_NODE) + { + segmentsx->sdata[index].next2=i; + break; + } + else + index=segmentsx->sdata[index].next2; + } + } + while(1); + } + + if(!((i+1)%10000)) + { + printf("\rIndexing Segments: Segments=%d",i+1); + fflush(stdout); + } + } + + /* Unmap from memory */ + + if(!option_slim) + { + nodesx->xdata=UnmapFile(nodesx->filename); + segmentsx->xdata=UnmapFile(segmentsx->filename); + } + + /* Print the final message */ + + printf("\rIndexed Segments: Segments=%d \n",segmentsx->number); + fflush(stdout); +} + + +/*++++++++++++++++++++++++++++++++++++++ + Save the node list to a file. + + NodesX* nodesx The set of nodes to save. + + const char *filename The name of the file to save. + ++++++++++++++++++++++++++++++++++++++*/ + +void SaveNodeList(NodesX* nodesx,const char *filename) +{ + index_t i; + int fd; + Nodes *nodes; + int super_number=0; + + /* Check the start conditions */ + + assert(nodesx->ndata); /* Must have ndata filled in => real nodes exist */ + + /* Print the start message */ + + printf("Writing Nodes: Nodes=0"); + fflush(stdout); + + /* Map into memory */ + + if(!option_slim) + nodesx->xdata=MapFile(nodesx->filename); + + /* Count the number of super-nodes */ + + for(i=0;inumber;i++) + if(nodesx->ndata[i].firstseg&NODE_SUPER) + super_number++; + + /* Fill in a Nodes structure with the offset of the real data in the file after + the Node structure itself. */ + + nodes=calloc(1,sizeof(Nodes)); + + assert(nodes); /* Check calloc() worked */ + + nodes->number=nodesx->number; + nodes->snumber=super_number; + + nodes->latbins=nodesx->latbins; + nodes->lonbins=nodesx->lonbins; + + nodes->latzero=nodesx->latzero; + nodes->lonzero=nodesx->lonzero; + + nodes->data=NULL; + nodes->offsets=NULL; + nodes->nodes=NULL; + + /* Write out the Nodes structure and then the real data. */ + + fd=OpenFile(filename); + + WriteFile(fd,nodes,sizeof(Nodes)); + + WriteFile(fd,nodesx->offsets,(nodesx->latbins*nodesx->lonbins+1)*sizeof(index_t)); + + for(i=0;inumber;i++) + { + NodeX *nodex=LookupNodeX(nodesx,i,1); + Node *node=&nodesx->ndata[nodex->id]; + + WriteFile(fd,node,sizeof(Node)); + + if(!((i+1)%10000)) + { + printf("\rWriting Nodes: Nodes=%d",i+1); + fflush(stdout); + } + } + + CloseFile(fd); + + /* Unmap from memory */ + + if(!option_slim) + nodesx->xdata=UnmapFile(nodesx->filename); + + /* Print the final message */ + + printf("\rWrote Nodes: Nodes=%d \n",nodes->number); + fflush(stdout); + + /* Free the fake Nodes */ + + free(nodes); +} diff --git a/src/nodesx.h b/src/nodesx.h new file mode 100644 index 0000000..2dc40a4 --- /dev/null +++ b/src/nodesx.h @@ -0,0 +1,100 @@ +/*************************************** + $Header: /home/amb/routino/src/RCS/nodesx.h,v 1.23 2010/03/19 19:47:09 amb Exp $ + + A header file for the extended nodes. + + Part of the Routino routing software. + ******************/ /****************** + This file Copyright 2008-2010 Andrew M. Bishop + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + ***************************************/ + + +#ifndef NODESX_H +#define NODESX_H /*+ To stop multiple inclusions. +*/ + +#include + +#include "typesx.h" +#include "types.h" + + +/* Data structures */ + + +/*+ An extended structure used for processing. +*/ +struct _NodeX +{ + node_t id; /*+ The node identifier. +*/ + + latlong_t latitude; /*+ The node latitude. +*/ + latlong_t longitude; /*+ The node longitude. +*/ +}; + +/*+ A structure containing a set of nodes (memory format). +*/ +struct _NodesX +{ + char *filename; /*+ The name of the temporary file. +*/ + int fd; /*+ The file descriptor of the temporary file. +*/ + + uint32_t xnumber; /*+ The number of unsorted extended nodes. +*/ + + NodeX *xdata; /*+ The extended node data (sorted). +*/ + NodeX cached[2]; /*+ Two cached nodes read from the file in slim mode. +*/ + + uint32_t number; /*+ How many entries are still useful? +*/ + + node_t *idata; /*+ The extended node IDs (sorted by ID). +*/ + + uint8_t *super; /*+ A marker for super nodes (same order sorted nodes). +*/ + + Node *ndata; /*+ The actual nodes (same order as geographically sorted nodes). +*/ + + uint32_t latbins; /*+ The number of bins containing latitude. +*/ + uint32_t lonbins; /*+ The number of bins containing longitude. +*/ + + ll_bin_t latzero; /*+ The bin number of the furthest south bin. +*/ + ll_bin_t lonzero; /*+ The bin number of the furthest west bin. +*/ + + uint32_t latlonbin; /*+ A temporary index into the offsets array. +*/ + + index_t *offsets; /*+ An array of offset to the first node in each bin. +*/ +}; + + +/* Functions */ + +NodesX *NewNodeList(int append); +void FreeNodeList(NodesX *nodesx,int keep); + +void SaveNodeList(NodesX *nodesx,const char *filename); + +index_t IndexNodeX(NodesX* nodesx,node_t id); +NodeX *LookupNodeX(NodesX* nodesx,index_t index,int position); + +void AppendNode(NodesX* nodesx,node_t id,double latitude,double longitude); + +void SortNodeList(NodesX *nodesx); + +void SortNodeListGeographically(NodesX* nodesx); + +void RemoveNonHighwayNodes(NodesX *nodesx,SegmentsX *segmentsx); + +void CreateRealNodes(NodesX *nodesx,int iteration); + +void IndexNodes(NodesX *nodesx,SegmentsX *segmentsx); + + +#endif /* NODESX_H */ diff --git a/src/optimiser.c b/src/optimiser.c new file mode 100644 index 0000000..cdb5986 --- /dev/null +++ b/src/optimiser.c @@ -0,0 +1,922 @@ +/*************************************** + $Header: /home/amb/routino/src/RCS/optimiser.c,v 1.87 2010/07/08 17:33:09 amb Exp $ + + Routing optimiser. + + Part of the Routino routing software. + ******************/ /****************** + This file Copyright 2008-2010 Andrew M. Bishop + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + ***************************************/ + + +#include + +#include "types.h" +#include "functions.h" +#include "nodes.h" +#include "segments.h" +#include "ways.h" +#include "results.h" + + +/*+ The option not to print any progress information. +*/ +extern int option_quiet; + +/*+ The option to calculate the quickest route insted of the shortest. +*/ +extern int option_quickest; + + +/*++++++++++++++++++++++++++++++++++++++ + Find the optimum route between two nodes not passing through a super-node. + + Results *FindNormalRoute Returns a set of results. + + Nodes *nodes The set of nodes to use. + + Segments *segments The set of segments to use. + + Ways *ways The set of ways to use. + + index_t start The start node. + + index_t finish The finish node. + + Profile *profile The profile containing the transport type, speeds and allowed highways. + ++++++++++++++++++++++++++++++++++++++*/ + +Results *FindNormalRoute(Nodes *nodes,Segments *segments,Ways *ways,index_t start,index_t finish,Profile *profile) +{ + Results *results; + Queue *queue; + index_t node1,node2; + score_t finish_score; + double finish_lat,finish_lon; + Result *result1,*result2; + Segment *segment; + Way *way; + + /* Set up the finish conditions */ + + finish_score=INF_SCORE; + + if(IsFakeNode(finish)) + GetFakeLatLong(finish,&finish_lat,&finish_lon); + else + GetLatLong(nodes,finish,&finish_lat,&finish_lon); + + /* Create the list of results and insert the first node into the queue */ + + results=NewResultsList(8); + + results->start=start; + results->finish=finish; + + result1=InsertResult(results,start); + + ZeroResult(result1); + + queue=NewQueueList(); + + InsertInQueue(queue,result1); + + /* Loop across all nodes in the queue */ + + while((result1=PopFromQueue(queue))) + { + if(result1->score>finish_score) + continue; + + node1=result1->node; + + if(IsFakeNode(node1)) + segment=FirstFakeSegment(node1); + else + segment=FirstSegment(segments,nodes,node1); + + while(segment) + { + score_t segment_pref,segment_score,cumulative_score; + int i; + + node2=OtherNode(segment,node1); /* need this here because we use node2 later */ + + if(!IsNormalSegment(segment)) + goto endloop; + + if(profile->oneway && IsOnewayTo(segment,node1)) + goto endloop; + + if(result1->prev==node2) + goto endloop; + + if(node2!=finish && !IsFakeNode(node2) && IsSuperNode(nodes,node2)) + goto endloop; + + way=LookupWay(ways,segment->way); + + if(!(way->allow&profile->allow)) + goto endloop; + + if(!profile->highway[HIGHWAY(way->type)]) + goto endloop; + + if(way->weight && way->weightweight) + goto endloop; + + if((way->height && way->heightheight) || + (way->width && way->width width ) || + (way->length && way->lengthlength)) + goto endloop; + + segment_pref=profile->highway[HIGHWAY(way->type)]; + + for(i=1;iprops & PROPERTIES(i)) + { + if(way->props & PROPERTIES(i)) + segment_pref*=profile->props_yes[i]; + else + segment_pref*=profile->props_no[i]; + } + + if(segment_pref==0) + goto endloop; + + if(option_quickest==0) + segment_score=(score_t)DISTANCE(segment->distance)/segment_pref; + else + segment_score=(score_t)Duration(segment,way,profile)/segment_pref; + + cumulative_score=result1->score+segment_score; + + if(cumulative_score>finish_score) + goto endloop; + + result2=FindResult(results,node2); + + if(!result2) /* New end node */ + { + result2=InsertResult(results,node2); + result2->prev=node1; + result2->next=NO_NODE; + result2->score=cumulative_score; + result2->segment=segment; + + if(node2==finish) + { + finish_score=cumulative_score; + } + else + { + result2->sortby=result2->score; + + InsertInQueue(queue,result2); + } + } + else if(cumulative_scorescore) /* New end node is better */ + { + result2->prev=node1; + result2->score=cumulative_score; + result2->segment=segment; + + if(node2==finish) + { + finish_score=cumulative_score; + } + else + { + result2->sortby=result2->score; + + if(result2->scorefinish)) + GetFakeLatLong(end->finish,&finish_lat,&finish_lon); + else + GetLatLong(nodes,end->finish,&finish_lat,&finish_lon); + + /* Create the list of results and insert the first node into the queue */ + + results=NewResultsList(2048); + + results->start=begin->start; + results->finish=end->finish; + + result1=InsertResult(results,begin->start); + result3=FindResult(begin,begin->start); + + *result1=*result3; + + queue=NewQueueList(); + + /* Insert the finish points of the beginning part of the path into the queue */ + + result3=FirstResult(begin); + + while(result3) + { + if(result3->node!=begin->start && !IsFakeNode(result3->node) && IsSuperNode(nodes,result3->node)) + { + result2=InsertResult(results,result3->node); + + *result2=*result3; + + result2->prev=begin->start; + + result2->sortby=result2->score; + + InsertInQueue(queue,result2); + } + + result3=NextResult(begin,result3); + } + + if(begin->number==1) + InsertInQueue(queue,result1); + + /* Loop across all nodes in the queue */ + + while((result1=PopFromQueue(queue))) + { + if(result1->score>finish_score) + continue; + + node1=result1->node; + + segment=FirstSegment(segments,nodes,node1); + + while(segment) + { + score_t segment_pref,segment_score,cumulative_score; + int i; + + if(!IsSuperSegment(segment)) + goto endloop; + + if(profile->oneway && IsOnewayTo(segment,node1)) + goto endloop; + + node2=OtherNode(segment,node1); + + if(result1->prev==node2) + goto endloop; + + way=LookupWay(ways,segment->way); + + if(!(way->allow&profile->allow)) + goto endloop; + + if(!profile->highway[HIGHWAY(way->type)]) + goto endloop; + + if(way->weight && way->weightweight) + goto endloop; + + if((way->height && way->heightheight) || + (way->width && way->width width ) || + (way->length && way->lengthlength)) + goto endloop; + + segment_pref=profile->highway[HIGHWAY(way->type)]; + + for(i=1;iprops & PROPERTIES(i)) + { + if(way->props & PROPERTIES(i)) + segment_pref*=profile->props_yes[i]; + else + segment_pref*=profile->props_no[i]; + } + + if(segment_pref==0) + goto endloop; + + if(option_quickest==0) + segment_score=(score_t)DISTANCE(segment->distance)/segment_pref; + else + segment_score=(score_t)Duration(segment,way,profile)/segment_pref; + + cumulative_score=result1->score+segment_score; + + if(cumulative_score>finish_score) + goto endloop; + + result2=FindResult(results,node2); + + if(!result2) /* New end node */ + { + result2=InsertResult(results,node2); + result2->prev=node1; + result2->next=NO_NODE; + result2->score=cumulative_score; + result2->segment=segment; + + if((result3=FindResult(end,node2))) + { + if((result2->score+result3->score)score+result3->score; + end_prev=node2; + } + } + else + { + double lat,lon; + distance_t direct; + + GetLatLong(nodes,node2,&lat,&lon); + direct=Distance(lat,lon,finish_lat,finish_lon); + + if(option_quickest==0) + result2->sortby=result2->score+(score_t)direct/profile->max_pref; + else + result2->sortby=result2->score+(score_t)distance_speed_to_duration(direct,profile->max_speed)/profile->max_pref; + + InsertInQueue(queue,result2); + } + } + else if(cumulative_scorescore) /* New end node is better */ + { + result2->prev=node1; + result2->score=cumulative_score; + result2->segment=segment; + + if((result3=FindResult(end,node2))) + { + if((result2->score+result3->score)score+result3->score; + end_prev=node2; + } + } + else if(result2->scoresortby=result2->score+(score_t)direct/profile->max_pref; + else + result2->sortby=result2->score+(score_t)distance_speed_to_duration(direct,profile->max_speed)/profile->max_pref; + + InsertInQueue(queue,result2); + } + } + + endloop: + + if(!option_quiet && !(results->number%10000)) + { + printf("\rRouting: Super-Nodes checked = %d",results->number); + fflush(stdout); + } + + segment=NextSegment(segments,segment,node1); + } + } + + if(!option_quiet) + { + printf("\rRouting: Super-Nodes checked = %d\n",results->number); + fflush(stdout); + } + + /* Finish off the end part of the route. */ + + if(!FindResult(results,end->finish) && end_prev!=NO_NODE) + { + result2=InsertResult(results,end->finish); + result3=FindResult(end,end->finish); + + *result2=*result3; + + result2->prev=end_prev; + result2->score=finish_score; + } + + FreeQueueList(queue); + + /* Check it worked */ + + if(end_prev==NO_NODE) + { + FreeResultsList(results); + return(NULL); + } + + FixForwardRoute(results,end->finish); + + return(results); +} + + +/*++++++++++++++++++++++++++++++++++++++ + Find all routes from a specified node to any super-node. + + Results *FindStartRoutes Returns a set of results. + + Nodes *nodes The set of nodes to use. + + Segments *segments The set of segments to use. + + Ways *ways The set of ways to use. + + index_t start The start node. + + Profile *profile The profile containing the transport type, speeds and allowed highways. + ++++++++++++++++++++++++++++++++++++++*/ + +Results *FindStartRoutes(Nodes *nodes,Segments *segments,Ways *ways,index_t start,Profile *profile) +{ + Results *results; + Queue *queue; + index_t node1,node2; + Result *result1,*result2; + Segment *segment; + Way *way; + + /* Insert the first node into the queue */ + + results=NewResultsList(8); + + results->start=start; + + result1=InsertResult(results,start); + + ZeroResult(result1); + + queue=NewQueueList(); + + InsertInQueue(queue,result1); + + /* Loop across all nodes in the queue */ + + while((result1=PopFromQueue(queue))) + { + node1=result1->node; + + if(IsFakeNode(node1)) + segment=FirstFakeSegment(node1); + else + segment=FirstSegment(segments,nodes,node1); + + while(segment) + { + score_t segment_pref,segment_score,cumulative_score; + int i; + + if(!IsNormalSegment(segment)) + goto endloop; + + if(profile->oneway && IsOnewayTo(segment,node1)) + goto endloop; + + node2=OtherNode(segment,node1); + + if(result1->prev==node2) + goto endloop; + + way=LookupWay(ways,segment->way); + + if(!(way->allow&profile->allow)) + goto endloop; + + if(!profile->highway[HIGHWAY(way->type)]) + goto endloop; + + if(way->weight && way->weightweight) + goto endloop; + + if((way->height && way->heightheight) || + (way->width && way->width width ) || + (way->length && way->lengthlength)) + goto endloop; + + segment_pref=profile->highway[HIGHWAY(way->type)]; + + for(i=1;iprops & PROPERTIES(i)) + { + if(way->props & PROPERTIES(i)) + segment_pref*=profile->props_yes[i]; + else + segment_pref*=profile->props_no[i]; + } + + if(segment_pref==0) + goto endloop; + + if(option_quickest==0) + segment_score=(score_t)DISTANCE(segment->distance)/segment_pref; + else + segment_score=(score_t)Duration(segment,way,profile)/segment_pref; + + cumulative_score=result1->score+segment_score; + + result2=FindResult(results,node2); + + if(!result2) /* New end node */ + { + result2=InsertResult(results,node2); + result2->prev=node1; + result2->next=NO_NODE; + result2->score=cumulative_score; + result2->segment=segment; + + if(!IsFakeNode(node2) && !IsSuperNode(nodes,node2)) + { + result2->sortby=result2->score; + InsertInQueue(queue,result2); + } + } + else if(cumulative_scorescore) /* New end node is better */ + { + result2->prev=node1; + result2->score=cumulative_score; + result2->segment=segment; + + if(!IsFakeNode(node2) && !IsSuperNode(nodes,node2)) + { + result2->sortby=result2->score; + InsertInQueue(queue,result2); + } + } + + endloop: + + if(IsFakeNode(node1)) + segment=NextFakeSegment(segment,node1); + else + segment=NextSegment(segments,segment,node1); + } + } + + FreeQueueList(queue); + + /* Check it worked */ + + if(results->number==1) + { + FreeResultsList(results); + return(NULL); + } + + return(results); +} + + +/*++++++++++++++++++++++++++++++++++++++ + Find all routes from any super-node to a specific node. + + Results *FindFinishRoutes Returns a set of results. + + Nodes *nodes The set of nodes to use. + + Segments *segments The set of segments to use. + + Ways *ways The set of ways to use. + + index_t finish The finishing node. + + Profile *profile The profile containing the transport type, speeds and allowed highways. + ++++++++++++++++++++++++++++++++++++++*/ + +Results *FindFinishRoutes(Nodes *nodes,Segments *segments,Ways *ways,index_t finish,Profile *profile) +{ + Results *results; + Queue *queue; + index_t node1,node2; + Result *result1,*result2; + Segment *segment; + Way *way; + + /* Insert the first node into the queue */ + + results=NewResultsList(8); + + results->finish=finish; + + result1=InsertResult(results,finish); + + ZeroResult(result1); + + queue=NewQueueList(); + + InsertInQueue(queue,result1); + + /* Loop across all nodes in the queue */ + + while((result1=PopFromQueue(queue))) + { + node1=result1->node; + + if(IsFakeNode(node1)) + segment=FirstFakeSegment(node1); + else + segment=FirstSegment(segments,nodes,node1); + + while(segment) + { + score_t segment_pref,segment_score,cumulative_score; + int i; + + if(!IsNormalSegment(segment)) + goto endloop; + + if(profile->oneway && IsOnewayFrom(segment,node1)) + goto endloop; + + node2=OtherNode(segment,node1); + + if(result1->next==node2) + goto endloop; + + way=LookupWay(ways,segment->way); + + if(!(way->allow&profile->allow)) + goto endloop; + + if(!profile->highway[HIGHWAY(way->type)]) + goto endloop; + + if(way->weight && way->weightweight) + goto endloop; + + if((way->height && way->heightheight) || + (way->width && way->width width ) || + (way->length && way->lengthlength)) + goto endloop; + + segment_pref=profile->highway[HIGHWAY(way->type)]; + + for(i=1;iprops & PROPERTIES(i)) + { + if(way->props & PROPERTIES(i)) + segment_pref*=profile->props_yes[i]; + else + segment_pref*=profile->props_no[i]; + } + + if(segment_pref==0) + goto endloop; + + if(option_quickest==0) + segment_score=(score_t)DISTANCE(segment->distance)/segment_pref; + else + segment_score=(score_t)Duration(segment,way,profile)/segment_pref; + + cumulative_score=result1->score+segment_score; + + result2=FindResult(results,node2); + + if(!result2) /* New end node */ + { + result2=InsertResult(results,node2); + result2->prev=NO_NODE; + result2->next=node1; + result2->score=cumulative_score; + result2->segment=segment; + + if(!IsFakeNode(node2) && !IsSuperNode(nodes,node2)) + { + result2->sortby=result2->score; + InsertInQueue(queue,result2); + } + } + else if(cumulative_scorescore) /* New end node is better */ + { + result2->next=node1; + result2->score=cumulative_score; + result2->segment=segment; + + if(!IsFakeNode(node2) && !IsSuperNode(nodes,node2)) + { + result2->sortby=result2->score; + InsertInQueue(queue,result2); + } + } + + endloop: + + if(IsFakeNode(node1)) + segment=NextFakeSegment(segment,node1); + else + segment=NextSegment(segments,segment,node1); + } + } + + FreeQueueList(queue); + + /* Check it worked */ + + if(results->number==1) + { + FreeResultsList(results); + return(NULL); + } + + return(results); +} + + +/*++++++++++++++++++++++++++++++++++++++ + Create an optimum route given the set of super-nodes to follow. + + Results *CombineRoutes Returns the results from joining the super-nodes. + + Results *results The set of results from the super-nodes. + + Nodes *nodes The list of nodes. + + Segments *segments The set of segments to use. + + Ways *ways The list of ways. + + Profile *profile The profile containing the transport type, speeds and allowed highways. + ++++++++++++++++++++++++++++++++++++++*/ + +Results *CombineRoutes(Results *results,Nodes *nodes,Segments *segments,Ways *ways,Profile *profile) +{ + Result *result1,*result2,*result3,*result4; + Results *combined; + + combined=NewResultsList(64); + + combined->start=results->start; + combined->finish=results->finish; + + /* Sort out the combined route */ + + result1=FindResult(results,results->start); + + result3=InsertResult(combined,results->start); + + ZeroResult(result3); + + do + { + if(result1->next!=NO_NODE) + { + Results *results2=FindNormalRoute(nodes,segments,ways,result1->node,result1->next,profile); + + result2=FindResult(results2,result1->node); + + result3->next=result2->next; + + result2=FindResult(results2,result2->next); + + do + { + result4=InsertResult(combined,result2->node); + + *result4=*result2; + result4->score+=result3->score; + + if(result2->next!=NO_NODE) + result2=FindResult(results2,result2->next); + else + result2=NULL; + } + while(result2); + + FreeResultsList(results2); + + result1=FindResult(results,result1->next); + + result3=result4; + } + else + result1=NULL; + } + while(result1); + + return(combined); +} + + +/*++++++++++++++++++++++++++++++++++++++ + Fx the forward route (i.e. setup next nodes for forward path from prev nodes on reverse path). + + Results *results The set of results to update. + + index_t finish The finish point. + ++++++++++++++++++++++++++++++++++++++*/ + +void FixForwardRoute(Results *results,index_t finish) +{ + Result *result2=FindResult(results,finish); + Result *result1; + + /* Create the forward links for the optimum path */ + + do + { + if(result2->prev!=NO_NODE) + { + index_t node1=result2->prev; + + result1=FindResult(results,node1); + + result1->next=result2->node; + + result2=result1; + } + else + result2=NULL; + } + while(result2); + + results->finish=finish; +} diff --git a/src/osmparser.c b/src/osmparser.c new file mode 100644 index 0000000..3af039d --- /dev/null +++ b/src/osmparser.c @@ -0,0 +1,748 @@ +/*************************************** + $Header: /home/amb/routino/src/RCS/osmparser.c,v 1.69 2010/05/29 13:54:23 amb Exp $ + + OSM XML file parser (either JOSM or planet) + + Part of the Routino routing software. + ******************/ /****************** + This file Copyright 2008-2010 Andrew M. Bishop + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + ***************************************/ + + +#include +#include +#include +#include + +#include "typesx.h" +#include "functionsx.h" +#include "nodesx.h" +#include "segmentsx.h" +#include "waysx.h" +#include "xmlparse.h" +#include "tagging.h" + + +/* Macros */ + +#define ISTRUE(xx) (!strcmp(xx,"true") || !strcmp(xx,"yes") || !strcmp(xx,"1")) + + +/* Local variables */ + +static long nnodes=0,nways=0,nrelations=0; +static TagList *current_tags=NULL; + +static node_t *way_nodes=NULL; +static int way_nnodes=0; + +static NodesX *nodes; +static SegmentsX *segments; +static WaysX *ways; + + +/* Local functions */ + +static void process_way_tags(TagList *tags,way_t id); + + +/* The XML tag processing function prototypes */ + +//static int xmlDeclaration_function(const char *_tag_,int _type_,const char *version,const char *encoding); +//static int osmType_function(const char *_tag_,int _type_); +static int relationType_function(const char *_tag_,int _type_,const char *id); +static int wayType_function(const char *_tag_,int _type_,const char *id); +//static int memberType_function(const char *_tag_,int _type_,const char *type,const char *ref,const char *role); +static int ndType_function(const char *_tag_,int _type_,const char *ref); +static int nodeType_function(const char *_tag_,int _type_,const char *id,const char *lat,const char *lon); +static int tagType_function(const char *_tag_,int _type_,const char *k,const char *v); +//static int boundType_function(const char *_tag_,int _type_); +//static int boundsType_function(const char *_tag_,int _type_); + + +/* The XML tag definitions */ + +/*+ The boundsType type tag. +*/ +static xmltag boundsType_tag= + {"bounds", + 0, {NULL}, + NULL, + {NULL}}; + +/*+ The boundType type tag. +*/ +static xmltag boundType_tag= + {"bound", + 0, {NULL}, + NULL, + {NULL}}; + +/*+ The tagType type tag. +*/ +static xmltag tagType_tag= + {"tag", + 2, {"k","v"}, + tagType_function, + {NULL}}; + +/*+ The nodeType type tag. +*/ +static xmltag nodeType_tag= + {"node", + 3, {"id","lat","lon"}, + nodeType_function, + {&tagType_tag,NULL}}; + +/*+ The ndType type tag. +*/ +static xmltag ndType_tag= + {"nd", + 1, {"ref"}, + ndType_function, + {NULL}}; + +/*+ The memberType type tag. +*/ +static xmltag memberType_tag= + {"member", + 3, {"type","ref","role"}, + NULL, + {NULL}}; + +/*+ The wayType type tag. +*/ +static xmltag wayType_tag= + {"way", + 1, {"id"}, + wayType_function, + {&ndType_tag,&tagType_tag,NULL}}; + +/*+ The relationType type tag. +*/ +static xmltag relationType_tag= + {"relation", + 1, {"id"}, + relationType_function, + {&memberType_tag,&tagType_tag,NULL}}; + +/*+ The osmType type tag. +*/ +static xmltag osmType_tag= + {"osm", + 0, {NULL}, + NULL, + {&boundsType_tag,&boundType_tag,&nodeType_tag,&wayType_tag,&relationType_tag,NULL}}; + +/*+ The xmlDeclaration type tag. +*/ +static xmltag xmlDeclaration_tag= + {"xml", + 2, {"version","encoding"}, + NULL, + {NULL}}; + + +/*+ The complete set of tags at the top level. +*/ +static xmltag *xml_toplevel_tags[]={&xmlDeclaration_tag,&osmType_tag,NULL}; + + +/* The XML tag processing functions */ + + +/*++++++++++++++++++++++++++++++++++++++ + The function that is called when the boundsType XSD type is seen + + int boundsType_function Returns 0 if no error occured or something else otherwise. + + const char *_tag_ Set to the name of the element tag that triggered this function call. + + int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag. + ++++++++++++++++++++++++++++++++++++++*/ + +//static int boundsType_function(const char *_tag_,int _type_) +//{ +// return(0); +//} + + +/*++++++++++++++++++++++++++++++++++++++ + The function that is called when the boundType XSD type is seen + + int boundType_function Returns 0 if no error occured or something else otherwise. + + const char *_tag_ Set to the name of the element tag that triggered this function call. + + int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag. + ++++++++++++++++++++++++++++++++++++++*/ + +//static int boundType_function(const char *_tag_,int _type_) +//{ +// return(0); +//} + + +/*++++++++++++++++++++++++++++++++++++++ + The function that is called when the tagType XSD type is seen + + int tagType_function Returns 0 if no error occured or something else otherwise. + + const char *_tag_ Set to the name of the element tag that triggered this function call. + + int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag. + + const char *k The contents of the 'k' attribute (or NULL if not defined). + + const char *v The contents of the 'v' attribute (or NULL if not defined). + ++++++++++++++++++++++++++++++++++++++*/ + +static int tagType_function(const char *_tag_,int _type_,const char *k,const char *v) +{ + if(_type_&XMLPARSE_TAG_START && current_tags) + { + XMLPARSE_ASSERT_STRING(_tag_,k); + XMLPARSE_ASSERT_STRING(_tag_,v); + + AppendTag(current_tags,k,v); + } + + return(0); +} + + +/*++++++++++++++++++++++++++++++++++++++ + The function that is called when the nodeType XSD type is seen + + int nodeType_function Returns 0 if no error occured or something else otherwise. + + const char *_tag_ Set to the name of the element tag that triggered this function call. + + int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag. + + const char *id The contents of the 'id' attribute (or NULL if not defined). + + const char *lat The contents of the 'lat' attribute (or NULL if not defined). + + const char *lon The contents of the 'lon' attribute (or NULL if not defined). + ++++++++++++++++++++++++++++++++++++++*/ + +static int nodeType_function(const char *_tag_,int _type_,const char *id,const char *lat,const char *lon) +{ + if(_type_&XMLPARSE_TAG_START) + { + node_t node_id; + double latitude,longitude; + + nnodes++; + + if(!(nnodes%1000)) + { + printf("\rReading: Lines=%ld Nodes=%ld Ways=%ld Relations=%ld",ParseXML_LineNumber(),nnodes,nways,nrelations); + fflush(stdout); + } + + /* Handle the node information */ + + XMLPARSE_ASSERT_STRING(_tag_,id); node_id=atoll(id); /* need long long conversion */ + XMLPARSE_ASSERT_FLOATING(_tag_,lat,latitude); + XMLPARSE_ASSERT_FLOATING(_tag_,lon,longitude); + + AppendNode(nodes,node_id,degrees_to_radians(latitude),degrees_to_radians(longitude)); + +// current_tags=NewTagList(); + current_tags=NULL; + } + +// if(_type_&XMLPARSE_TAG_END) +// { +// TagList *result=ApplyTaggingRules(&NodeRules,current_tags); +// +// DeleteTagList(current_tags); +// DeleteTagList(result); +// } + + return(0); +} + + +/*++++++++++++++++++++++++++++++++++++++ + The function that is called when the ndType XSD type is seen + + int ndType_function Returns 0 if no error occured or something else otherwise. + + const char *_tag_ Set to the name of the element tag that triggered this function call. + + int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag. + + const char *ref The contents of the 'ref' attribute (or NULL if not defined). + ++++++++++++++++++++++++++++++++++++++*/ + +static int ndType_function(const char *_tag_,int _type_,const char *ref) +{ + if(_type_&XMLPARSE_TAG_START) + { + node_t node_id; + + XMLPARSE_ASSERT_STRING(_tag_,ref); node_id=atoll(ref); /* need long long conversion */ + + if((way_nnodes%256)==0) + way_nodes=(node_t*)realloc((void*)way_nodes,(way_nnodes+256)*sizeof(node_t)); + + way_nodes[way_nnodes++]=node_id; + } + + return(0); +} + + +/*++++++++++++++++++++++++++++++++++++++ + The function that is called when the memberType XSD type is seen + + int memberType_function Returns 0 if no error occured or something else otherwise. + + const char *_tag_ Set to the name of the element tag that triggered this function call. + + int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag. + + const char *type The contents of the 'type' attribute (or NULL if not defined). + + const char *ref The contents of the 'ref' attribute (or NULL if not defined). + + const char *role The contents of the 'role' attribute (or NULL if not defined). + ++++++++++++++++++++++++++++++++++++++*/ + +//static int memberType_function(const char *_tag_,int _type_,const char *type,const char *ref,const char *role) +//{ +// return(0); +//} + + +/*++++++++++++++++++++++++++++++++++++++ + The function that is called when the wayType XSD type is seen + + int wayType_function Returns 0 if no error occured or something else otherwise. + + const char *_tag_ Set to the name of the element tag that triggered this function call. + + int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag. + + const char *id The contents of the 'id' attribute (or NULL if not defined). + ++++++++++++++++++++++++++++++++++++++*/ + +static int wayType_function(const char *_tag_,int _type_,const char *id) +{ + static way_t way_id; + + if(_type_&XMLPARSE_TAG_START) + { + nways++; + + if(!(nways%1000)) + { + printf("\rReading: Lines=%ld Nodes=%ld Ways=%ld Relations=%ld",ParseXML_LineNumber(),nnodes,nways,nrelations); + fflush(stdout); + } + + current_tags=NewTagList(); + way_nnodes=0; + + /* Handle the way information */ + + XMLPARSE_ASSERT_STRING(_tag_,id); way_id=atoll(id); /* need long long conversion */ + } + + if(_type_&XMLPARSE_TAG_END) + { + TagList *result=ApplyTaggingRules(&WayRules,current_tags); + + process_way_tags(result,way_id); + + DeleteTagList(current_tags); + DeleteTagList(result); + } + + return(0); +} + + +/*++++++++++++++++++++++++++++++++++++++ + The function that is called when the relationType XSD type is seen + + int relationType_function Returns 0 if no error occured or something else otherwise. + + const char *_tag_ Set to the name of the element tag that triggered this function call. + + int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag. + + const char *id The contents of the 'id' attribute (or NULL if not defined). + ++++++++++++++++++++++++++++++++++++++*/ + +static int relationType_function(const char *_tag_,int _type_,const char *id) +{ + if(_type_&XMLPARSE_TAG_START) + { + nrelations++; + + if(!(nrelations%1000)) + { + printf("\rReading: Lines=%ld Nodes=%ld Ways=%ld Relations=%ld",ParseXML_LineNumber(),nnodes,nways,nrelations); + fflush(stdout); + } + +// current_tags=NewTagList(); + current_tags=NULL; + } + +// if(_type_&XMLPARSE_TAG_END) +// { +// TagList *result=ApplyTaggingRules(&RelationRules,current_tags); +// +// DeleteTagList(current_tags); +// DeleteTagList(result); +// } + + return(0); +} + + +/*++++++++++++++++++++++++++++++++++++++ + The function that is called when the osmType XSD type is seen + + int osmType_function Returns 0 if no error occured or something else otherwise. + + const char *_tag_ Set to the name of the element tag that triggered this function call. + + int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag. + ++++++++++++++++++++++++++++++++++++++*/ + +//static int osmType_function(const char *_tag_,int _type_) +//{ +// return(0); +//} + + +/*++++++++++++++++++++++++++++++++++++++ + The function that is called when the XML declaration is seen + + int xmlDeclaration_function Returns 0 if no error occured or something else otherwise. + + const char *_tag_ Set to the name of the element tag that triggered this function call. + + int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag. + + const char *version The contents of the 'version' attribute (or NULL if not defined). + + const char *encoding The contents of the 'encoding' attribute (or NULL if not defined). + ++++++++++++++++++++++++++++++++++++++*/ + +//static int xmlDeclaration_function(const char *_tag_,int _type_,const char *version,const char *encoding) +//{ +// return(0); +//} + + +/*++++++++++++++++++++++++++++++++++++++ + Parse an OSM XML file (from JOSM or planet download). + + int ParseOSM Returns 0 if OK or something else in case of an error. + + FILE *file The file to read from. + + NodesX *OSMNodes The array of nodes to fill in. + + SegmentsX *OSMSegments The array of segments to fill in. + + WaysX *OSMWays The arrray of ways to fill in. + ++++++++++++++++++++++++++++++++++++++*/ + +int ParseOSM(FILE *file,NodesX *OSMNodes,SegmentsX *OSMSegments,WaysX *OSMWays) +{ + int retval; + + /* Parse the file */ + + nodes=OSMNodes; + segments=OSMSegments; + ways=OSMWays; + + nnodes=0,nways=0,nrelations=0; + + printf("\rReading: Lines=0 Nodes=0 Ways=0 Relations=0"); + fflush(stdout); + + retval=ParseXML(file,xml_toplevel_tags,XMLPARSE_UNKNOWN_ATTR_IGNORE); + + printf("\rRead: Lines=%ld Nodes=%ld Ways=%ld Relations=%ld \n",ParseXML_LineNumber(),nnodes,nways,nrelations); + fflush(stdout); + + return(retval); +} + + +/*++++++++++++++++++++++++++++++++++++++ + Process the tags associated with a way. + + TagList *tags The list of way tags. + + way_t id The id of the way. + ++++++++++++++++++++++++++++++++++++++*/ + +static void process_way_tags(TagList *tags,way_t id) +{ + Way way={0}; + int oneway=0,roundabout=0; + char *name=NULL,*ref=NULL; + + int i; + + /* Parse the tags */ + + for(i=0;intags;i++) + { + char *k=tags->k[i]; + char *v=tags->v[i]; + + switch(*k) + { + case 'b': + if(!strcmp(k,"bicycle")) + if(ISTRUE(v)) + way.allow|= Allow_Bicycle; + + if(!strcmp(k,"bridge")) + if(ISTRUE(v)) + way.props|=Properties_Bridge; + + break; + + case 'f': + if(!strcmp(k,"foot")) + if(ISTRUE(v)) + way.allow|= Allow_Foot; + + break; + + case 'g': + if(!strcmp(k,"goods")) + if(ISTRUE(v)) + way.allow|=Allow_Goods; + + break; + + case 'h': + if(!strcmp(k,"highway")) + way.type=HighwayType(v); + + if(!strcmp(k,"horse")) + if(ISTRUE(v)) + way.allow|=Allow_Horse; + + if(!strcmp(k,"hgv")) + if(ISTRUE(v)) + way.allow|=Allow_HGV; + + break; + + case 'j': + if(!strcmp(k,"junction") && !strcmp(v,"roundabout")) + roundabout=1; + + break; + + case 'm': + if(!strcmp(k,"maxspeed")) + { + if(strstr(v,"mph")) + way.speed=kph_to_speed(1.609*atof(v)); + else + way.speed=kph_to_speed(atof(v)); + } + + if(!strcmp(k,"maxweight")) + { + if(strstr(v,"kg")) + way.weight=tonnes_to_weight(atof(v)/1000); + else + way.weight=tonnes_to_weight(atof(v)); + } + + if(!strcmp(k,"maxheight")) + { + if(strchr(v,'\'')) + { + int feet,inches; + + if(sscanf(v,"%d'%d\"",&feet,&inches)==2) + way.height=metres_to_height((feet+(double)inches/12.0)*0.254); + else if(sscanf(v,"%d'",&feet)==1) + way.height=metres_to_height((feet+(double)inches/12.0)*0.254); + } + else if(strstr(v,"ft") || strstr(v,"feet")) + way.height=metres_to_height(atof(v)*0.254); + else + way.height=metres_to_height(atof(v)); + } + + if(!strcmp(k,"maxwidth")) + { + if(strchr(v,'\'')) + { + int feet,inches; + + if(sscanf(v,"%d'%d\"",&feet,&inches)==2) + way.width=metres_to_height((feet+(double)inches/12.0)*0.254); + else if(sscanf(v,"%d'",&feet)==1) + way.width=metres_to_height((feet+(double)inches/12.0)*0.254); + } + else if(strstr(v,"ft") || strstr(v,"feet")) + way.width=metres_to_width(atof(v)*0.254); + else + way.width=metres_to_width(atof(v)); + } + + if(!strcmp(k,"maxlength")) + { + if(strchr(v,'\'')) + { + int feet,inches; + + if(sscanf(v,"%d'%d\"",&feet,&inches)==2) + way.length=metres_to_height((feet+(double)inches/12.0)*0.254); + else if(sscanf(v,"%d'",&feet)==1) + way.length=metres_to_height((feet+(double)inches/12.0)*0.254); + } + else if(strstr(v,"ft") || strstr(v,"feet")) + way.length=metres_to_length(atof(v)*0.254); + else + way.length=metres_to_length(atof(v)); + } + + if(!strcmp(k,"moped")) + if(ISTRUE(v)) + way.allow|=Allow_Moped; + + if(!strcmp(k,"motorbike")) + if(ISTRUE(v)) + way.allow|=Allow_Motorbike; + + if(!strcmp(k,"motorcar")) + if(ISTRUE(v)) + way.allow|=Allow_Motorcar; + + if(!strcmp(k,"multilane")) + if(ISTRUE(v)) + way.props|=Properties_Multilane; + + break; + + case 'n': + if(!strcmp(k,"name")) + name=v; + + break; + + case 'o': + if(!strcmp(k,"oneway")) + { + if(ISTRUE(v)) + oneway=1; + else if(!strcmp(v,"-1")) + oneway=-1; + } + + break; + + case 'p': + if(!strcmp(k,"paved")) + if(ISTRUE(v)) + way.props|=Properties_Paved; + + if(!strcmp(k,"psv")) + if(ISTRUE(v)) + way.allow|=Allow_PSV; + + break; + + case 'r': + if(!strcmp(k,"ref")) + ref=v; + + break; + + case 't': + if(!strcmp(k,"tunnel")) + if(ISTRUE(v)) + way.props|=Properties_Tunnel; + + break; + + case 'w': + if(!strcmp(k,"wheelchair")) + if(ISTRUE(v)) + way.allow|=Allow_Wheelchair; + + break; + + default: + ; + } + } + + /* Create the way */ + + if(way.type>0 && way.type0) + { + AppendSegment(segments,id,from,to,ONEWAY_1TO2); + AppendSegment(segments,id,to,from,ONEWAY_2TO1); + } + else if(oneway<0) + { + AppendSegment(segments,id,from,to,ONEWAY_2TO1); + AppendSegment(segments,id,to,from,ONEWAY_1TO2); + } + else + { + AppendSegment(segments,id,from,to,0); + AppendSegment(segments,id,to,from,0); + } + } + } + } +} diff --git a/src/output.c b/src/output.c new file mode 100644 index 0000000..e40f750 --- /dev/null +++ b/src/output.c @@ -0,0 +1,816 @@ +/*************************************** + $Header: /home/amb/routino/src/RCS/output.c,v 1.33 2010/07/07 17:31:06 amb Exp $ + + Routing output generator. + + Part of the Routino routing software. + ******************/ /****************** + This file Copyright 2008-2010 Andrew M. Bishop + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + ***************************************/ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "types.h" +#include "functions.h" +#include "translations.h" +#include "nodes.h" +#include "segments.h" +#include "ways.h" +#include "results.h" +#include "xmlparse.h" + + +/* Global variables */ + +/*+ The option to calculate the quickest route insted of the shortest. +*/ +extern int option_quickest; + +/*+ The options to select the format of the output. +*/ +extern int option_html,option_gpx_track,option_gpx_route,option_text,option_text_all; + +/* Local variables */ + +/*+ Heuristics for determining if a junction is important. +*/ +static char junction_other_way[Way_Count][Way_Count]= + { /* M, T, P, S, T, U, R, S, T, C, P, S = Way type of route not taken */ + { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, /* Motorway */ + { 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, /* Trunk */ + { 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, /* Primary */ + { 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0 }, /* Secondary */ + { 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0 }, /* Tertiary */ + { 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0 }, /* Unclassified */ + { 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0 }, /* Residential */ + { 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0 }, /* Service */ + { 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0 }, /* Track */ + { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0 }, /* Cycleway */ + { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, /* Path */ + { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, /* Steps */ + }; + + +/* Local functions */ + +static int turn_angle(Nodes *nodes,Segment *segment1,Segment *segment2,index_t node); +static int bearing_angle(Nodes *nodes,Segment *segment,index_t node); + + +/*++++++++++++++++++++++++++++++++++++++ + Print the optimum route between two nodes. + + Results **results The set of results to print (some may be NULL - ignore them). + + int nresults The number of results in the list. + + Nodes *nodes The list of nodes. + + Segments *segments The set of segments to use. + + Ways *ways The list of ways. + + Profile *profile The profile containing the transport type, speeds and allowed highways. + ++++++++++++++++++++++++++++++++++++++*/ + +void PrintRoute(Results **results,int nresults,Nodes *nodes,Segments *segments,Ways *ways,Profile *profile) +{ + FILE *htmlfile=NULL,*gpxtrackfile=NULL,*gpxroutefile=NULL,*textfile=NULL,*textallfile=NULL; + + int point=1; + distance_t cum_distance=0; + duration_t cum_duration=0; + double finish_lat,finish_lon; + int segment_count=0; + int route_count=0; + + /* Open the files */ + + if(option_quickest==0) + { + /* Print the result for the shortest route */ + + if(option_html) + htmlfile =fopen("shortest.html","w"); + if(option_gpx_track) + gpxtrackfile=fopen("shortest-track.gpx","w"); + if(option_gpx_route) + gpxroutefile=fopen("shortest-route.gpx","w"); + if(option_text) + textfile =fopen("shortest.txt","w"); + if(option_text_all) + textallfile =fopen("shortest-all.txt","w"); + + if(option_html && !htmlfile) + fprintf(stderr,"Warning: Cannot open file 'shortest.html' for writing [%s].\n",strerror(errno)); + if(option_gpx_track && !gpxtrackfile) + fprintf(stderr,"Warning: Cannot open file 'shortest-track.gpx' for writing [%s].\n",strerror(errno)); + if(option_gpx_route && !gpxroutefile) + fprintf(stderr,"Warning: Cannot open file 'shortest-route.gpx' for writing [%s].\n",strerror(errno)); + if(option_text && !textfile) + fprintf(stderr,"Warning: Cannot open file 'shortest.txt' for writing [%s].\n",strerror(errno)); + if(option_text_all && !textallfile) + fprintf(stderr,"Warning: Cannot open file 'shortest-all.txt' for writing [%s].\n",strerror(errno)); + } + else + { + /* Print the result for the quickest route */ + + if(option_html) + htmlfile =fopen("quickest.html","w"); + if(option_gpx_track) + gpxtrackfile=fopen("quickest-track.gpx","w"); + if(option_gpx_route) + gpxroutefile=fopen("quickest-route.gpx","w"); + if(option_text) + textfile =fopen("quickest.txt","w"); + if(option_text_all) + textallfile =fopen("quickest-all.txt","w"); + + if(option_html && !htmlfile) + fprintf(stderr,"Warning: Cannot open file 'quickest.html' for writing [%s].\n",strerror(errno)); + if(option_gpx_track && !gpxtrackfile) + fprintf(stderr,"Warning: Cannot open file 'quickest-track.gpx' for writing [%s].\n",strerror(errno)); + if(option_gpx_route && !gpxroutefile) + fprintf(stderr,"Warning: Cannot open file 'quickest-route.gpx' for writing [%s].\n",strerror(errno)); + if(option_text && !textfile) + fprintf(stderr,"Warning: Cannot open file 'quickest.txt' for writing [%s].\n",strerror(errno)); + if(option_text_all && !textallfile) + fprintf(stderr,"Warning: Cannot open file 'quickest-all.txt' for writing [%s].\n",strerror(errno)); + } + + /* Print the head of the files */ + + if(htmlfile) + { + fprintf(htmlfile,"\n"); + fprintf(htmlfile,"\n"); + if(translate_copyright_creator[0] && translate_copyright_creator[1]) + fprintf(htmlfile,"\n",translate_copyright_creator[0],translate_copyright_creator[1]); + if(translate_copyright_source[0] && translate_copyright_source[1]) + fprintf(htmlfile,"\n",translate_copyright_source[0],translate_copyright_source[1]); + if(translate_copyright_license[0] && translate_copyright_license[1]) + fprintf(htmlfile,"\n",translate_copyright_license[0],translate_copyright_license[1]); + fprintf(htmlfile,"\n"); + fprintf(htmlfile,""); + fprintf(htmlfile,translate_html_title,option_quickest?translate_route_quickest:translate_route_shortest); + fprintf(htmlfile,"\n"); + fprintf(htmlfile,"\n"); + fprintf(htmlfile,"\n"); + fprintf(htmlfile,"\n"); + fprintf(htmlfile,"

"); + fprintf(htmlfile,translate_html_title,option_quickest?translate_route_quickest:translate_route_shortest); + fprintf(htmlfile,"

\n"); + fprintf(htmlfile,"\n"); + } + + if(gpxtrackfile) + { + fprintf(gpxtrackfile,"\n"); + fprintf(gpxtrackfile,"\n"); + + fprintf(gpxtrackfile,"\n"); + fprintf(gpxtrackfile,"%s : %s\n",translate_copyright_creator[0],translate_copyright_creator[1]); + if(translate_copyright_source[1]) + { + fprintf(gpxtrackfile,"\n",translate_copyright_source[1]); + + if(translate_copyright_license[1]) + fprintf(gpxtrackfile,"%s\n",translate_copyright_license[1]); + + fprintf(gpxtrackfile,"\n"); + } + fprintf(gpxtrackfile,"\n"); + + fprintf(gpxtrackfile,"\n"); + fprintf(gpxtrackfile,""); + fprintf(gpxtrackfile,translate_gpx_name,option_quickest?translate_route_quickest:translate_route_shortest); + fprintf(gpxtrackfile,"\n"); + fprintf(gpxtrackfile,""); + fprintf(gpxtrackfile,translate_gpx_desc,option_quickest?translate_route_quickest:translate_route_shortest); + fprintf(gpxtrackfile,"\n"); + } + + if(gpxroutefile) + { + fprintf(gpxroutefile,"\n"); + fprintf(gpxroutefile,"\n"); + + fprintf(gpxroutefile,"\n"); + fprintf(gpxroutefile,"%s : %s\n",translate_copyright_creator[0],translate_copyright_creator[1]); + if(translate_copyright_source[1]) + { + fprintf(gpxroutefile,"\n",translate_copyright_source[1]); + + if(translate_copyright_license[1]) + fprintf(gpxroutefile,"%s\n",translate_copyright_license[1]); + + fprintf(gpxroutefile,"\n"); + } + fprintf(gpxroutefile,"\n"); + + fprintf(gpxroutefile,"\n"); + fprintf(gpxroutefile,""); + fprintf(gpxroutefile,translate_gpx_name,option_quickest?translate_route_quickest:translate_route_shortest); + fprintf(gpxroutefile,"\n"); + fprintf(gpxroutefile,""); + fprintf(gpxroutefile,translate_gpx_desc,option_quickest?translate_route_quickest:translate_route_shortest); + fprintf(gpxroutefile,"\n"); + } + + if(textfile) + { + if(translate_copyright_creator[0] && translate_copyright_creator[1]) + fprintf(textfile,"# %s : %s\n",translate_copyright_creator[0],translate_copyright_creator[1]); + if(translate_copyright_source[0] && translate_copyright_source[1]) + fprintf(textfile,"# %s : %s\n",translate_copyright_source[0],translate_copyright_source[1]); + if(translate_copyright_license[0] && translate_copyright_license[1]) + fprintf(textfile,"# %s : %s\n",translate_copyright_license[0],translate_copyright_license[1]); + if((translate_copyright_creator[0] && translate_copyright_creator[1]) || + (translate_copyright_source[0] && translate_copyright_source[1]) || + (translate_copyright_license[0] && translate_copyright_license[1])) + fprintf(textfile,"#\n"); + + fprintf(textfile,"#Latitude\tLongitude\tSection \tSection \tTotal \tTotal \tPoint\tTurn\tBearing\tHighway\n"); + fprintf(textfile,"# \t \tDistance\tDuration\tDistance\tDuration\tType \t \t \t \n"); + /* "%10.6f\t%11.6f\t%6.3f km\t%4.1f min\t%5.1f km\t%4.0f min\t%s\t %+d\t %+d\t%s\n" */ + } + + if(textallfile) + { + if(translate_copyright_creator[0] && translate_copyright_creator[1]) + fprintf(textallfile,"# %s : %s\n",translate_copyright_creator[0],translate_copyright_creator[1]); + if(translate_copyright_source[0] && translate_copyright_source[1]) + fprintf(textallfile,"# %s : %s\n",translate_copyright_source[0],translate_copyright_source[1]); + if(translate_copyright_license[0] && translate_copyright_license[1]) + fprintf(textallfile,"# %s : %s\n",translate_copyright_license[0],translate_copyright_license[1]); + if((translate_copyright_creator[0] && translate_copyright_creator[1]) || + (translate_copyright_source[0] && translate_copyright_source[1]) || + (translate_copyright_license[0] && translate_copyright_license[1])) + fprintf(textallfile,"#\n"); + + fprintf(textallfile,"#Latitude\tLongitude\t Node\tType\tSegment\tSegment\tTotal\tTotal \tSpeed\tBearing\tHighway\n"); + fprintf(textallfile,"# \t \t \t \tDist \tDurat'n\tDist \tDurat'n\t \t \t \n"); + /* "%10.6f\t%11.6f\t%8d%c\t%s\t%5.3f\t%5.2f\t%5.2f\t%5.1f\t%3d\t%4d\t%s\n" */ + } + + /* Loop through the segments of the route and print it */ + + while(!results[point]) + point++; + + while(point<=nresults) + { + int nextpoint=point; + double start_lat,start_lon; + distance_t junc_distance=0; + duration_t junc_duration=0; + Result *result; + + if(gpxtrackfile) + fprintf(gpxtrackfile,"\n"); + + if(IsFakeNode(results[point]->start)) + GetFakeLatLong(results[point]->start,&start_lat,&start_lon); + else + GetLatLong(nodes,results[point]->start,&start_lat,&start_lon); + + if(IsFakeNode(results[point]->finish)) + GetFakeLatLong(results[point]->finish,&finish_lat,&finish_lon); + else + GetLatLong(nodes,results[point]->finish,&finish_lat,&finish_lon); + + result=FindResult(results[point],results[point]->start); + + do + { + double latitude,longitude; + Result *nextresult; + + if(result->node==results[point]->start) + {latitude=start_lat; longitude=start_lon;} + else if(result->node==results[point]->finish) + {latitude=finish_lat; longitude=finish_lon;} + else + GetLatLong(nodes,result->node,&latitude,&longitude); + + if(gpxtrackfile) + fprintf(gpxtrackfile,"\n", + radians_to_degrees(latitude),radians_to_degrees(longitude)); + + nextresult=FindResult(results[point],result->next); + + if(!nextresult) + for(nextpoint=point+1;nextpoint<=nresults;nextpoint++) + if(results[nextpoint]) + { + nextresult=FindResult(results[nextpoint],results[nextpoint]->start); + nextresult=FindResult(results[nextpoint],nextresult->next); + break; + } + + if(result->node!=results[point]->start) + { + distance_t seg_distance=0; + duration_t seg_duration=0; + Way *resultway; + int important=0; + + /* Cache the values to be printed rather than calculating them repeatedly for each output format */ + + char *waynameraw=NULL,*wayname=NULL,*waynamexml=NULL; + int bearing_int=0,bearing_next_int=0,turn_int=0; + char *bearing_str=NULL,*bearing_next_str=NULL,*turn_str=NULL; + + /* Get the properties of this segment */ + + resultway=LookupWay(ways,result->segment->way); + + seg_distance+=DISTANCE(result->segment->distance); + seg_duration+=Duration(result->segment,resultway,profile); + junc_distance+=seg_distance; + junc_duration+=seg_duration; + cum_distance+=seg_distance; + cum_duration+=seg_duration; + + /* Decide if this is an important junction */ + + if(result->node==results[point]->finish) + important=10; + else + { + Segment *segment=FirstSegment(segments,nodes,result->node); + + do + { + index_t othernode=OtherNode(segment,result->node); + + if(othernode!=result->prev && segment!=result->segment) + if(IsNormalSegment(segment) && (!profile->oneway || !IsOnewayTo(segment,result->node))) + { + Way *way=LookupWay(ways,segment->way); + + if(othernode==result->next) /* the next segment that we follow */ + { + if(HIGHWAY(way->type)!=HIGHWAY(resultway->type)) + if(important<2) + important=2; + } + else /* a segment that we don't follow */ + { + if(junction_other_way[HIGHWAY(resultway->type)-1][HIGHWAY(way->type)-1]) + if(important<3) + important=3; + + if(important<1) + important=1; + } + } + + segment=NextSegment(segments,segment,result->node); + } + while(segment); + } + + /* Print out the important points (junctions / waypoints) */ + + if(important>1) + { + /* Print the intermediate finish points (because they have correct junction distances) */ + + if(htmlfile) + { + char *type; + + if(important==10) + type=translate_html_waypoint; + else + type=translate_html_junction; + + if(!waynameraw) + { + waynameraw=WayNameRaw(ways,resultway); + if(!*waynameraw) + waynameraw=translate_highway[HIGHWAY(resultway->type)]; + } + + if(!waynamexml) + waynamexml=ParseXML_Encode_Safe_XML(waynameraw); + + fprintf(htmlfile,"
%s:",translate_html_segment[0]); + fprintf(htmlfile,translate_html_segment[1], + waynamexml, + distance_to_km(junc_distance),duration_to_minutes(junc_duration)); + fprintf(htmlfile," ["); + fprintf(htmlfile,translate_html_total[1], + distance_to_km(cum_distance),duration_to_minutes(cum_duration)); + fprintf(htmlfile,"]\n"); + + fprintf(htmlfile,"
%.6f %.6f\n", + radians_to_degrees(latitude),radians_to_degrees(longitude)); + + if(nextresult) + { + if(!turn_str) + { + turn_int=turn_angle(nodes,result->segment,nextresult->segment,result->node); + turn_str=translate_turn[(4+(22+turn_int)/45)%8]; + } + + if(!bearing_next_str) + { + bearing_next_int=bearing_angle(nodes,nextresult->segment,nextresult->node); + bearing_next_str=translate_heading[(4+(22+bearing_next_int)/45)%8]; + } + + fprintf(htmlfile,"
%s:",translate_html_node[0]); + fprintf(htmlfile,translate_html_node[1], + type, + turn_str, + bearing_next_str); + fprintf(htmlfile,"\n"); + } + else + { + fprintf(htmlfile,"
%s:",translate_html_stop[0]); + fprintf(htmlfile,translate_html_stop[1], + translate_html_waypoint); + fprintf(htmlfile,"\n"); + fprintf(htmlfile,"
%s:",translate_html_total[0]); + fprintf(htmlfile,translate_html_total[1], + distance_to_km(cum_distance),duration_to_minutes(cum_duration)); + fprintf(htmlfile,"\n"); + } + } + + if(gpxroutefile) + { + if(!waynameraw) + { + waynameraw=WayNameRaw(ways,resultway); + if(!*waynameraw) + waynameraw=translate_highway[HIGHWAY(resultway->type)]; + } + + if(!waynamexml) + waynamexml=ParseXML_Encode_Safe_XML(waynameraw); + + if(!bearing_str) + { + bearing_int=bearing_angle(nodes,result->segment,result->node); + bearing_str=translate_heading[(4+(22+bearing_int)/45)%8]; + } + + fprintf(gpxroutefile,""); + fprintf(gpxroutefile,translate_gpx_step, + bearing_str, + waynamexml, + distance_to_km(junc_distance),duration_to_minutes(junc_duration)); + fprintf(gpxroutefile,"\n"); + + if(!nextresult) + { + fprintf(gpxroutefile,"%s\n", + radians_to_degrees(finish_lat),radians_to_degrees(finish_lon), + translate_gpx_finish); + fprintf(gpxroutefile,""); + fprintf(gpxroutefile,translate_gpx_final, + distance_to_km(cum_distance),duration_to_minutes(cum_duration)); + fprintf(gpxroutefile,"\n"); + } + else if(important==10) + fprintf(gpxroutefile,"%s%d\n", + radians_to_degrees(latitude),radians_to_degrees(longitude), + translate_gpx_inter,++segment_count); + else + fprintf(gpxroutefile,"%s%03d\n", + radians_to_degrees(latitude),radians_to_degrees(longitude), + translate_gpx_trip,++route_count); + } + + if(textfile) + { + char *type; + + if(important==10) + type="Waypt"; + else + type="Junct"; + + if(!wayname) + wayname=(char*)WayNameHighway(ways,resultway); + + if(nextresult) + { + if(!turn_str) + { + turn_int=turn_angle(nodes,result->segment,nextresult->segment,result->node); + turn_str=translate_turn[(4+(22+turn_int)/45)%8]; + } + + if(!bearing_next_str) + { + bearing_next_int=bearing_angle(nodes,nextresult->segment,nextresult->node); + bearing_next_str=translate_heading[(4+(22+bearing_next_int)/45)%8]; + } + + fprintf(textfile,"%10.6f\t%11.6f\t%6.3f km\t%4.1f min\t%5.1f km\t%4.0f min\t%s\t %+d\t %+d\t%s\n", + radians_to_degrees(latitude),radians_to_degrees(longitude), + distance_to_km(junc_distance),duration_to_minutes(junc_duration), + distance_to_km(cum_distance),duration_to_minutes(cum_duration), + type, + (22+turn_int)/45, + ((22+bearing_next_int)/45+4)%8-4, + wayname); + } + else + fprintf(textfile,"%10.6f\t%11.6f\t%6.3f km\t%4.1f min\t%5.1f km\t%4.0f min\t%s\t\t\t%s\n", + radians_to_degrees(latitude),radians_to_degrees(longitude), + distance_to_km(junc_distance),duration_to_minutes(junc_duration), + distance_to_km(cum_distance),duration_to_minutes(cum_duration), + type, + wayname); + } + + junc_distance=0; + junc_duration=0; + } + + /* Print out all of the results */ + + if(textallfile) + { + char *type; + + if(important==10) + type="Waypt"; + else if(important==2) + type="Change"; + else if(important>=1) + type="Junct"; + else + type="Inter"; + + if(!wayname) + wayname=(char*)WayNameHighway(ways,resultway); + + if(!bearing_str) + { + bearing_int=bearing_angle(nodes,result->segment,result->node); + bearing_str=translate_heading[(4+(22+bearing_int)/45)%8]; + } + + fprintf(textallfile,"%10.6f\t%11.6f\t%8d%c\t%s\t%5.3f\t%5.2f\t%5.2f\t%5.1f\t%3d\t%4d\t%s\n", + radians_to_degrees(latitude),radians_to_degrees(longitude), + IsFakeNode(result->node)?-(result->node&(~NODE_SUPER)):result->node, + (!IsFakeNode(result->node) && IsSuperNode(nodes,result->node))?'*':' ',type, + distance_to_km(seg_distance),duration_to_minutes(seg_duration), + distance_to_km(cum_distance),duration_to_minutes(cum_duration), + profile->speed[HIGHWAY(resultway->type)], + bearing_int, + wayname); + } + + if(waynamexml && waynamexml!=waynameraw) + free(waynamexml); + } + else if(!cum_distance) + { + int bearing_next_int=bearing_angle(nodes,nextresult->segment,nextresult->node); + char *bearing_next_str=translate_heading[(4+(22+bearing_next_int)/45)%8]; + + /* Print out the very first start point */ + + if(htmlfile) + { + fprintf(htmlfile,"
%.6f %.6f\n", + radians_to_degrees(latitude),radians_to_degrees(longitude)); + fprintf(htmlfile,"
%s:",translate_html_start[0]); + fprintf(htmlfile,translate_html_start[1], + translate_html_waypoint, + bearing_next_str); + fprintf(htmlfile,"\n"); + } + + if(gpxroutefile) + fprintf(gpxroutefile,"%s\n", + radians_to_degrees(latitude),radians_to_degrees(longitude), + translate_gpx_start); + + if(textfile) + fprintf(textfile,"%10.6f\t%11.6f\t%6.3f km\t%4.1f min\t%5.1f km\t%4.0f min\t%s\t\t +%d\t\n", + radians_to_degrees(latitude),radians_to_degrees(longitude), + 0.0,0.0,0.0,0.0, + "Waypt", + (22+bearing_next_int)/45); + + if(textallfile) + fprintf(textallfile,"%10.6f\t%11.6f\t%8d%c\t%s\t%5.3f\t%5.2f\t%5.2f\t%5.1f\t\t\t\n", + radians_to_degrees(latitude),radians_to_degrees(longitude), + IsFakeNode(result->node)?-(result->node&(~NODE_SUPER)):result->node, + (!IsFakeNode(result->node) && IsSuperNode(nodes,result->node))?'*':' ',"Waypt", + 0.0,0.0,0.0,0.0); + } + + result=nextresult; + } + while(point==nextpoint); + + if(gpxtrackfile) + fprintf(gpxtrackfile,"\n"); + + point=nextpoint; + } + + /* Print the tail of the files */ + + if(htmlfile) + { + fprintf(htmlfile,"
\n"); + + if((translate_copyright_creator[0] && translate_copyright_creator[1]) || + (translate_copyright_source[0] && translate_copyright_source[1]) || + (translate_copyright_license[0] && translate_copyright_license[1])) + { + fprintf(htmlfile,"

\n"); + fprintf(htmlfile,"\n"); + if(translate_copyright_creator[0] && translate_copyright_creator[1]) + fprintf(htmlfile,"
%s:%s\n",translate_copyright_creator[0],translate_copyright_creator[1]); + if(translate_copyright_source[0] && translate_copyright_source[1]) + fprintf(htmlfile,"
%s:%s\n",translate_copyright_source[0],translate_copyright_source[1]); + if(translate_copyright_license[0] && translate_copyright_license[1]) + fprintf(htmlfile,"
%s:%s\n",translate_copyright_license[0],translate_copyright_license[1]); + fprintf(htmlfile,"
\n"); + } + + fprintf(htmlfile,"\n"); + fprintf(htmlfile,"\n"); + } + + if(gpxtrackfile) + { + fprintf(gpxtrackfile,"\n"); + fprintf(gpxtrackfile,"\n"); + } + + if(gpxroutefile) + { + fprintf(gpxroutefile,"\n"); + fprintf(gpxroutefile,"\n"); + } + + /* Close the files */ + + if(htmlfile) + fclose(htmlfile); + if(gpxtrackfile) + fclose(gpxtrackfile); + if(gpxroutefile) + fclose(gpxroutefile); + if(textfile) + fclose(textfile); + if(textallfile) + fclose(textallfile); +} + + +/*++++++++++++++++++++++++++++++++++++++ + Calculate the angle to turn at a junction from segment1 to segment2 at node. + + int turn_angle Returns a value in the range -4 to +4 indicating the angle to turn. + + Nodes *nodes The set of nodes. + + Segment *segment1 The current segment. + + Segment *segment2 The next segment. + + index_t node The node at which they join. + + Straight ahead is zero, turning to the right is positive (90 degrees) and turning to the left is negative. + Angles are calculated using flat Cartesian lat/long grid approximation (after scaling longitude due to latitude). + ++++++++++++++++++++++++++++++++++++++*/ + +static int turn_angle(Nodes *nodes,Segment *segment1,Segment *segment2,index_t node) +{ + double lat1,latm,lat2; + double lon1,lonm,lon2; + double angle1,angle2,angle; + index_t node1,node2; + + node1=OtherNode(segment1,node); + node2=OtherNode(segment2,node); + + if(IsFakeNode(node1)) + GetFakeLatLong(node1,&lat1,&lon1); + else + GetLatLong(nodes,node1,&lat1,&lon1); + + if(IsFakeNode(node)) + GetFakeLatLong(node,&latm,&lonm); + else + GetLatLong(nodes,node,&latm,&lonm); + + if(IsFakeNode(node2)) + GetFakeLatLong(node2,&lat2,&lon2); + else + GetLatLong(nodes,node2,&lat2,&lon2); + + angle1=atan2((lonm-lon1)*cos(latm),(latm-lat1)); + angle2=atan2((lon2-lonm)*cos(latm),(lat2-latm)); + + angle=angle2-angle1; + + angle=radians_to_degrees(angle); + + angle=round(angle); + + if(angle<-180) angle+=360; + if(angle> 180) angle-=360; + + return((int)angle); +} + + +/*++++++++++++++++++++++++++++++++++++++ + Calculate the bearing of a segment from the given node. + + int bearing_angle Returns a value in the range 0 to 359 indicating the bearing. + + Nodes *nodes The set of nodes. + + Segment *segment The segment. + + index_t node The node to start. + + Angles are calculated using flat Cartesian lat/long grid approximation (after scaling longitude due to latitude). + ++++++++++++++++++++++++++++++++++++++*/ + +static int bearing_angle(Nodes *nodes,Segment *segment,index_t node) +{ + double lat1,lat2; + double lon1,lon2; + double angle; + index_t node1,node2; + + node1=node; + node2=OtherNode(segment,node); + + if(IsFakeNode(node1)) + GetFakeLatLong(node1,&lat1,&lon1); + else + GetLatLong(nodes,node1,&lat1,&lon1); + + if(IsFakeNode(node2)) + GetFakeLatLong(node2,&lat2,&lon2); + else + GetLatLong(nodes,node2,&lat2,&lon2); + + angle=atan2((lat2-lat1),(lon2-lon1)*cos(lat1)); + + angle=radians_to_degrees(angle); + + angle=round(270-angle); + + if(angle< 0) angle+=360; + if(angle>360) angle-=360; + + return((int)angle); +} diff --git a/src/planetsplitter.c b/src/planetsplitter.c new file mode 100644 index 0000000..5380455 --- /dev/null +++ b/src/planetsplitter.c @@ -0,0 +1,410 @@ +/*************************************** + $Header: /home/amb/routino/src/RCS/planetsplitter.c,v 1.73 2010/05/22 18:40:47 amb Exp $ + + OSM planet file splitter. + + Part of the Routino routing software. + ******************/ /****************** + This file Copyright 2008-2010 Andrew M. Bishop + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + ***************************************/ + + +#include +#include +#include +#include + +#include "typesx.h" +#include "types.h" +#include "functionsx.h" +#include "functions.h" +#include "nodesx.h" +#include "segmentsx.h" +#include "waysx.h" +#include "superx.h" +#include "ways.h" +#include "tagging.h" + + +/* Global variables */ + +/*+ The option to use a slim mode with file-backed read-only intermediate storage. +*/ +int option_slim=0; + +/*+ The name of the temporary directory. +*/ +char *option_tmpdirname=NULL; + +/*+ The amount of RAM to use for filesorting. +*/ +size_t option_filesort_ramsize=0; + + +/* Local functions */ + +static void print_usage(int detail); + + +/*++++++++++++++++++++++++++++++++++++++ + The main program for the planetsplitter. + ++++++++++++++++++++++++++++++++++++++*/ + +int main(int argc,char** argv) +{ + NodesX *Nodes; + SegmentsX *Segments,*SuperSegments=NULL,*MergedSegments=NULL; + WaysX *Ways; + int iteration=0,quit=0; + int max_iterations=10; + char *dirname=NULL,*prefix=NULL,*tagging=NULL; + int option_parse_only=0,option_process_only=0; + int option_filenames=0; + int arg; + + /* Parse the command line arguments */ + + for(arg=1;arg9?"=":""); + fflush(stdout); + + if(iteration==0) + { + /* Select the super-nodes */ + + ChooseSuperNodes(Nodes,Segments,Ways); + + /* Select the super-segments */ + + SuperSegments=CreateSuperSegments(Nodes,Segments,Ways,iteration); + } + else + { + SegmentsX *SuperSegments2; + + /* Select the super-nodes */ + + ChooseSuperNodes(Nodes,SuperSegments,Ways); + + /* Select the super-segments */ + + SuperSegments2=CreateSuperSegments(Nodes,SuperSegments,Ways,iteration); + + if(SuperSegments->xnumber==SuperSegments2->xnumber) + quit=1; + + FreeSegmentList(SuperSegments,0); + + SuperSegments=SuperSegments2; + } + + /* Sort the super-segments */ + + SortSegmentList(SuperSegments); + + /* Remove duplicated super-segments */ + + DeduplicateSegments(SuperSegments,Nodes,Ways); + + iteration++; + + if(iteration>max_iterations) + quit=1; + } + while(!quit); + + /* Combine the super-segments */ + + printf("\nCombine Segments and Super-Segments\n===================================\n\n"); + fflush(stdout); + + /* Merge the super-segments */ + + MergedSegments=MergeSuperSegments(Segments,SuperSegments); + + FreeSegmentList(Segments,0); + + FreeSegmentList(SuperSegments,0); + + Segments=MergedSegments; + + /* Rotate segments so that node1] [--prefix=]\n" + " [--slim] [--sort-ram-size=]\n" + " [--tmpdir=]\n" + " [--parse-only | --process-only]\n" + " [--max-iterations=]\n" + " [--tagging=]\n" + " [ ...]\n"); + + if(detail) + fprintf(stderr, + "\n" + "--help Prints this information.\n" + "\n" + "--dir= The directory containing the routing database.\n" + "--prefix= The filename prefix for the routing database.\n" + "\n" + "--slim Use less RAM and more temporary files.\n" + "--sort-ram-size= The amount of RAM (in MB) to use for data sorting\n" + " (defaults to 64MB with '--slim' or 256MB otherwise.)\n" + "--tmpdir= The directory name for temporary files.\n" + " (defaults to the '--dir' option directory.)\n" + "\n" + "--parse-only Parse the input OSM files and store the results.\n" + "--process-only Process the stored results from previous option.\n" + "\n" + "--max-iterations= The number of iterations for finding super-nodes.\n" + "\n" + "--tagging= The name of the XML file containing the tagging rules\n" + " (defaults to 'tagging.xml' with '--dirname' and\n" + " '--prefix' options).\n" + "\n" + " ... The name(s) of the file(s) to process (by default\n" + " data is read from standard input).\n" + "\n" + " defaults to all but can be set to:\n" + "%s" + "\n" + " can be selected from:\n" + "%s" + "\n" + " can be selected from:\n" + "%s", + TransportList(),HighwayList(),PropertyList()); + + exit(!detail); +} diff --git a/src/profiles.c b/src/profiles.c new file mode 100644 index 0000000..e27a067 --- /dev/null +++ b/src/profiles.c @@ -0,0 +1,1018 @@ +/*************************************** + $Header: /home/amb/routino/src/RCS/profiles.c,v 1.42 2010/05/29 10:37:12 amb Exp $ + + Load the profiles from a file and the functions for handling them. + + Part of the Routino routing software. + ******************/ /****************** + This file Copyright 2008-2010 Andrew M. Bishop + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + ***************************************/ + + +#include +#include +#include + +#include "profiles.h" +#include "types.h" +#include "ways.h" +#include "xmlparse.h" +#include "functions.h" + + +/*+ The profiles that have been loaded from file. +*/ +static Profile **loaded_profiles=NULL; + +/*+ The number of profiles that have been loaded from file. +*/ +static int nloaded_profiles=0; + + +/* The XML tag processing function prototypes */ + +//static int xmlDeclaration_function(const char *_tag_,int _type_,const char *version,const char *encoding); +//static int RoutinoProfilesType_function(const char *_tag_,int _type_); +static int profileType_function(const char *_tag_,int _type_,const char *name,const char *transport); +//static int restrictionsType_function(const char *_tag_,int _type_); +static int lengthType_function(const char *_tag_,int _type_,const char *limit); +static int widthType_function(const char *_tag_,int _type_,const char *limit); +static int heightType_function(const char *_tag_,int _type_,const char *limit); +static int weightType_function(const char *_tag_,int _type_,const char *limit); +//static int propertiesType_function(const char *_tag_,int _type_); +static int onewayType_function(const char *_tag_,int _type_,const char *obey); +static int propertyType_function(const char *_tag_,int _type_,const char *type,const char *percent); +//static int preferencesType_function(const char *_tag_,int _type_); +static int preferenceType_function(const char *_tag_,int _type_,const char *highway,const char *percent); +//static int speedsType_function(const char *_tag_,int _type_); +static int speedType_function(const char *_tag_,int _type_,const char *highway,const char *kph); + + +/* The XML tag definitions */ + +/*+ The speedType type tag. +*/ +static xmltag speedType_tag= + {"speed", + 2, {"highway","kph"}, + speedType_function, + {NULL}}; + +/*+ The speedsType type tag. +*/ +static xmltag speedsType_tag= + {"speeds", + 0, {NULL}, + NULL, + {&speedType_tag,NULL}}; + +/*+ The preferenceType type tag. +*/ +static xmltag preferenceType_tag= + {"preference", + 2, {"highway","percent"}, + preferenceType_function, + {NULL}}; + +/*+ The preferencesType type tag. +*/ +static xmltag preferencesType_tag= + {"preferences", + 0, {NULL}, + NULL, + {&preferenceType_tag,NULL}}; + +/*+ The propertyType type tag. +*/ +static xmltag propertyType_tag= + {"property", + 2, {"type","percent"}, + propertyType_function, + {NULL}}; + +/*+ The onewayType type tag. +*/ +static xmltag onewayType_tag= + {"oneway", + 1, {"obey"}, + onewayType_function, + {NULL}}; + +/*+ The propertiesType type tag. +*/ +static xmltag propertiesType_tag= + {"properties", + 0, {NULL}, + NULL, + {&propertyType_tag,NULL}}; + +/*+ The weightType type tag. +*/ +static xmltag weightType_tag= + {"weight", + 1, {"limit"}, + weightType_function, + {NULL}}; + +/*+ The heightType type tag. +*/ +static xmltag heightType_tag= + {"height", + 1, {"limit"}, + heightType_function, + {NULL}}; + +/*+ The widthType type tag. +*/ +static xmltag widthType_tag= + {"width", + 1, {"limit"}, + widthType_function, + {NULL}}; + +/*+ The lengthType type tag. +*/ +static xmltag lengthType_tag= + {"length", + 1, {"limit"}, + lengthType_function, + {NULL}}; + +/*+ The restrictionsType type tag. +*/ +static xmltag restrictionsType_tag= + {"restrictions", + 0, {NULL}, + NULL, + {&onewayType_tag,&weightType_tag,&heightType_tag,&widthType_tag,&lengthType_tag,NULL}}; + +/*+ The profileType type tag. +*/ +static xmltag profileType_tag= + {"profile", + 2, {"name","transport"}, + profileType_function, + {&speedsType_tag,&preferencesType_tag,&propertiesType_tag,&restrictionsType_tag,NULL}}; + +/*+ The RoutinoProfilesType type tag. +*/ +static xmltag RoutinoProfilesType_tag= + {"routino-profiles", + 0, {NULL}, + NULL, + {&profileType_tag,NULL}}; + +/*+ The xmlDeclaration type tag. +*/ +static xmltag xmlDeclaration_tag= + {"xml", + 2, {"version","encoding"}, + NULL, + {NULL}}; + + +/*+ The complete set of tags at the top level. +*/ +static xmltag *xml_toplevel_tags[]={&xmlDeclaration_tag,&RoutinoProfilesType_tag,NULL}; + + +/* The XML tag processing functions */ + + +/*++++++++++++++++++++++++++++++++++++++ + The function that is called when the speedType XSD type is seen + + int speedType_function Returns 0 if no error occured or something else otherwise. + + const char *_tag_ Set to the name of the element tag that triggered this function call. + + int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag. + + const char *highway The contents of the 'highway' attribute (or NULL if not defined). + + const char *kph The contents of the 'kph' attribute (or NULL if not defined). + ++++++++++++++++++++++++++++++++++++++*/ + +static int speedType_function(const char *_tag_,int _type_,const char *highway,const char *kph) +{ + if(_type_&XMLPARSE_TAG_START) + { + double speed; + Highway highwaytype; + + XMLPARSE_ASSERT_STRING(_tag_,highway); + + highwaytype=HighwayType(highway); + + if(highwaytype==Way_Count) + XMLPARSE_INVALID(_tag_,highway); + + XMLPARSE_ASSERT_FLOATING(_tag_,kph,speed); + + loaded_profiles[nloaded_profiles-1]->speed[highwaytype]=kph_to_speed(speed); + } + + return(0); +} + + +/*++++++++++++++++++++++++++++++++++++++ + The function that is called when the speedsType XSD type is seen + + int speedsType_function Returns 0 if no error occured or something else otherwise. + + const char *_tag_ Set to the name of the element tag that triggered this function call. + + int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag. + ++++++++++++++++++++++++++++++++++++++*/ + +//static int speedsType_function(const char *_tag_,int _type_) +//{ +// return(0); +//} + + +/*++++++++++++++++++++++++++++++++++++++ + The function that is called when the preferenceType XSD type is seen + + int preferenceType_function Returns 0 if no error occured or something else otherwise. + + const char *_tag_ Set to the name of the element tag that triggered this function call. + + int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag. + + const char *highway The contents of the 'highway' attribute (or NULL if not defined). + + const char *percent The contents of the 'percent' attribute (or NULL if not defined). + ++++++++++++++++++++++++++++++++++++++*/ + +static int preferenceType_function(const char *_tag_,int _type_,const char *highway,const char *percent) +{ + if(_type_&XMLPARSE_TAG_START) + { + Highway highwaytype; + double p; + + XMLPARSE_ASSERT_STRING(_tag_,highway); + + highwaytype=HighwayType(highway); + + if(highwaytype==Way_Count) + XMLPARSE_INVALID(_tag_,highway); + + XMLPARSE_ASSERT_FLOATING(_tag_,percent,p); + + loaded_profiles[nloaded_profiles-1]->highway[highwaytype]=p; + } + + return(0); +} + + +/*++++++++++++++++++++++++++++++++++++++ + The function that is called when the preferencesType XSD type is seen + + int preferencesType_function Returns 0 if no error occured or something else otherwise. + + const char *_tag_ Set to the name of the element tag that triggered this function call. + + int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag. + ++++++++++++++++++++++++++++++++++++++*/ + +//static int preferencesType_function(const char *_tag_,int _type_) +//{ +// return(0); +//} + + +/*++++++++++++++++++++++++++++++++++++++ + The function that is called when the propertyType XSD type is seen + + int propertyType_function Returns 0 if no error occured or something else otherwise. + + const char *_tag_ Set to the name of the element tag that triggered this function call. + + int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag. + + const char *type The contents of the 'type' attribute (or NULL if not defined). + + const char *percent The contents of the 'percent' attribute (or NULL if not defined). + ++++++++++++++++++++++++++++++++++++++*/ + +static int propertyType_function(const char *_tag_,int _type_,const char *type,const char *percent) +{ + if(_type_&XMLPARSE_TAG_START) + { + Property property; + double p; + + XMLPARSE_ASSERT_STRING(_tag_,type); + + property=PropertyType(type); + + if(property==Property_Count) + XMLPARSE_INVALID(_tag_,type); + + XMLPARSE_ASSERT_FLOATING(_tag_,percent,p); + + loaded_profiles[nloaded_profiles-1]->props_yes[property]=p; + } + + return(0); +} + + +/*++++++++++++++++++++++++++++++++++++++ + The function that is called when the onewayType XSD type is seen + + int onewayType_function Returns 0 if no error occured or something else otherwise. + + const char *_tag_ Set to the name of the element tag that triggered this function call. + + int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag. + + const char *obey The contents of the 'obey' attribute (or NULL if not defined). + ++++++++++++++++++++++++++++++++++++++*/ + +static int onewayType_function(const char *_tag_,int _type_,const char *obey) +{ + if(_type_&XMLPARSE_TAG_START) + { + int o; + + XMLPARSE_ASSERT_INTEGER(_tag_,obey,o); + + loaded_profiles[nloaded_profiles-1]->oneway=!!o; + } + + return(0); +} + + +/*++++++++++++++++++++++++++++++++++++++ + The function that is called when the propertiesType XSD type is seen + + int propertiesType_function Returns 0 if no error occured or something else otherwise. + + const char *_tag_ Set to the name of the element tag that triggered this function call. + + int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag. + ++++++++++++++++++++++++++++++++++++++*/ + +//static int propertiesType_function(const char *_tag_,int _type_) +//{ +// return(0); +//} + + +/*++++++++++++++++++++++++++++++++++++++ + The function that is called when the weightType XSD type is seen + + int weightType_function Returns 0 if no error occured or something else otherwise. + + const char *_tag_ Set to the name of the element tag that triggered this function call. + + int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag. + + const char *limit The contents of the 'limit' attribute (or NULL if not defined). + ++++++++++++++++++++++++++++++++++++++*/ + +static int weightType_function(const char *_tag_,int _type_,const char *limit) +{ + if(_type_&XMLPARSE_TAG_START) + { + double l; + + XMLPARSE_ASSERT_FLOATING(_tag_,limit,l); + + loaded_profiles[nloaded_profiles-1]->weight=tonnes_to_weight(l); + } + + return(0); +} + + +/*++++++++++++++++++++++++++++++++++++++ + The function that is called when the heightType XSD type is seen + + int heightType_function Returns 0 if no error occured or something else otherwise. + + const char *_tag_ Set to the name of the element tag that triggered this function call. + + int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag. + + const char *limit The contents of the 'limit' attribute (or NULL if not defined). + ++++++++++++++++++++++++++++++++++++++*/ + +static int heightType_function(const char *_tag_,int _type_,const char *limit) +{ + if(_type_&XMLPARSE_TAG_START) + { + double l; + + XMLPARSE_ASSERT_FLOATING(_tag_,limit,l); + + loaded_profiles[nloaded_profiles-1]->height=metres_to_height(l); + } + + return(0); +} + + +/*++++++++++++++++++++++++++++++++++++++ + The function that is called when the widthType XSD type is seen + + int widthType_function Returns 0 if no error occured or something else otherwise. + + const char *_tag_ Set to the name of the element tag that triggered this function call. + + int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag. + + const char *limit The contents of the 'limit' attribute (or NULL if not defined). + ++++++++++++++++++++++++++++++++++++++*/ + +static int widthType_function(const char *_tag_,int _type_,const char *limit) +{ + if(_type_&XMLPARSE_TAG_START) + { + double l; + + XMLPARSE_ASSERT_FLOATING(_tag_,limit,l); + + loaded_profiles[nloaded_profiles-1]->width=metres_to_width(l); + } + + return(0); +} + + +/*++++++++++++++++++++++++++++++++++++++ + The function that is called when the lengthType XSD type is seen + + int lengthType_function Returns 0 if no error occured or something else otherwise. + + const char *_tag_ Set to the name of the element tag that triggered this function call. + + int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag. + + const char *limit The contents of the 'limit' attribute (or NULL if not defined). + ++++++++++++++++++++++++++++++++++++++*/ + +static int lengthType_function(const char *_tag_,int _type_,const char *limit) +{ + if(_type_&XMLPARSE_TAG_START) + { + double l; + + XMLPARSE_ASSERT_FLOATING(_tag_,limit,l); + + loaded_profiles[nloaded_profiles-1]->length=metres_to_length(l); + } + + return(0); +} + + +/*++++++++++++++++++++++++++++++++++++++ + The function that is called when the restrictionsType XSD type is seen + + int restrictionsType_function Returns 0 if no error occured or something else otherwise. + + const char *_tag_ Set to the name of the element tag that triggered this function call. + + int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag. + ++++++++++++++++++++++++++++++++++++++*/ + +//static int restrictionsType_function(const char *_tag_,int _type_) +//{ +// return(0); +//} + + +/*++++++++++++++++++++++++++++++++++++++ + The function that is called when the profileType XSD type is seen + + int profileType_function Returns 0 if no error occured or something else otherwise. + + const char *_tag_ Set to the name of the element tag that triggered this function call. + + int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag. + + const char *name The contents of the 'name' attribute (or NULL if not defined). + + const char *transport The contents of the 'transport' attribute (or NULL if not defined). + ++++++++++++++++++++++++++++++++++++++*/ + +static int profileType_function(const char *_tag_,int _type_,const char *name,const char *transport) +{ + if(_type_&XMLPARSE_TAG_START) + { + Transport transporttype; + int i; + + XMLPARSE_ASSERT_STRING(_tag_,name); + XMLPARSE_ASSERT_STRING(_tag_,transport); + + for(i=0;iname)) + XMLPARSE_MESSAGE(_tag_,"profile name must be unique"); + + transporttype=TransportType(transport); + + if(transporttype==Transport_None) + XMLPARSE_INVALID(_tag_,transport); + + if((nloaded_profiles%16)==0) + loaded_profiles=(Profile**)realloc((void*)loaded_profiles,(nloaded_profiles+16)*sizeof(Profile*)); + + nloaded_profiles++; + + loaded_profiles[nloaded_profiles-1]=(Profile*)calloc(1,sizeof(Profile)); + + loaded_profiles[nloaded_profiles-1]->name=strcpy(malloc(strlen(name)+1),name); + loaded_profiles[nloaded_profiles-1]->transport=transporttype; + } + + return(0); +} + + +/*++++++++++++++++++++++++++++++++++++++ + The function that is called when the RoutinoProfilesType XSD type is seen + + int RoutinoProfilesType_function Returns 0 if no error occured or something else otherwise. + + const char *_tag_ Set to the name of the element tag that triggered this function call. + + int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag. + ++++++++++++++++++++++++++++++++++++++*/ + +//static int RoutinoProfilesType_function(const char *_tag_,int _type_) +//{ +// return(0); +//} + + +/*++++++++++++++++++++++++++++++++++++++ + The function that is called when the XML declaration is seen + + int xmlDeclaration_function Returns 0 if no error occured or something else otherwise. + + const char *_tag_ Set to the name of the element tag that triggered this function call. + + int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag. + + const char *version The contents of the 'version' attribute (or NULL if not defined). + + const char *encoding The contents of the 'encoding' attribute (or NULL if not defined). + ++++++++++++++++++++++++++++++++++++++*/ + +//static int xmlDeclaration_function(const char *_tag_,int _type_,const char *version,const char *encoding) +//{ +// return(0); +//} + + +/*++++++++++++++++++++++++++++++++++++++ + The XML profile parser. + + int ParseXMLProfiles Returns 0 if OK or something else in case of an error. + + const char *filename The name of the file to read. + ++++++++++++++++++++++++++++++++++++++*/ + +int ParseXMLProfiles(const char *filename) +{ + int retval; + + if(!ExistsFile(filename)) + { + fprintf(stderr,"Error: Specified profiles file '%s' does not exist.\n",filename); + return(1); + } + + FILE *file=fopen(filename,"r"); + + if(!file) + { + fprintf(stderr,"Error: Cannot open profiles file '%s' for reading.\n",filename); + return(1); + } + + retval=ParseXML(file,xml_toplevel_tags,XMLPARSE_UNKNOWN_ATTR_ERRNONAME); + + fclose(file); + + if(retval) + { + int i; + + for(i=0;iname,name)) + return(loaded_profiles[i]); + + return(NULL); +} + + +/*++++++++++++++++++++++++++++++++++++++ + Update a profile with highway preference scaling factor. + + int UpdateProfile Returns 1 in case of a problem. + + Profile *profile The profile to be updated. + + Ways *ways The set of ways to use. + ++++++++++++++++++++++++++++++++++++++*/ + +int UpdateProfile(Profile *profile,Ways *ways) +{ + score_t hmax=0; + int i; + + /* Fix up the allowed transport types. */ + + profile->allow=ALLOWED(profile->transport); + + if(!(profile->allow & ways->allow)) + return(1); + + /* Normalise the highway preferences into the range 0 -> 1 */ + + for(i=1;ihighway[i]<0) + profile->highway[i]=0; + + if(profile->highway[i]>hmax) + hmax=profile->highway[i]; + } + + if(hmax==0) + return(1); + + for(i=1;ihighway[i]/=hmax; + + /* Normalise the property preferences into the range 0 -> 2 */ + + for(i=1;iprops_yes[i]<0) + profile->props_yes[i]=0; + + if(profile->props_yes[i]>100) + profile->props_yes[i]=100; + + profile->props_yes[i]/=50; + profile->props_no [i] =2-profile->props_yes[i]; + } + + /* Find the fastest preferred speed */ + + profile->max_speed=0; + + for(i=1;ispeed[i]>profile->max_speed) + profile->max_speed=profile->speed[i]; + + if(profile->max_speed==0) + return(1); + + /* Find the most preferred property combination */ + + profile->max_pref=1; /* since highway prefs were normalised to 1 */ + + for(i=1;iprops & PROPERTIES(i)) + { + if(profile->props_yes[i]>profile->props_no[i]) + profile->max_pref*=profile->props_yes[i]; + else + profile->max_pref*=profile->props_no[i]; + } + + return(0); +} + + +/*++++++++++++++++++++++++++++++++++++++ + Print out a profile. + + const Profile *profile The profile to print. + ++++++++++++++++++++++++++++++++++++++*/ + +void PrintProfile(const Profile *profile) +{ + unsigned int i; + + printf("Profile\n=======\n"); + + printf("\n"); + + printf("Transport: %s\n",TransportName(profile->transport)); + + printf("\n"); + + for(i=1;ihighway[i]); + + printf("\n"); + + for(i=1;ihighway[i]) + printf("Speed on %-12s: %3d km/h / %2.0f mph\n",HighwayName(i),profile->speed[i],(double)profile->speed[i]/1.6); + + printf("\n"); + + for(i=1;iprops_yes[i]); + + printf("\n"); + + printf("Obey one-way : %s\n",profile->oneway?"yes":"no"); + printf("Minimum weight: %.1f tonnes\n",weight_to_tonnes(profile->weight)); + printf("Minimum height: %.1f metres\n",height_to_metres(profile->height)); + printf("Minimum width : %.1f metres\n",width_to_metres(profile->width)); + printf("Minimum length: %.1f metres\n",length_to_metres(profile->length)); +} + + +/*++++++++++++++++++++++++++++++++++++++ + Print out the profiles as XML for use as program input. + ++++++++++++++++++++++++++++++++++++++*/ + +void PrintProfilesXML(void) +{ + unsigned int i,j; + char *padding=" "; + + printf("\n"); + printf("\n"); + + printf("\n"); + printf("\n"); + + for(j=0;j\n",loaded_profiles[j]->name,TransportName(loaded_profiles[j]->transport)); + + printf(" \n"); + for(i=1;i\n",HighwayName(i),padding+3+strlen(HighwayName(i)),loaded_profiles[j]->speed[i]); + printf(" \n"); + + printf(" \n"); + for(i=1;i\n",HighwayName(i),padding+3+strlen(HighwayName(i)),loaded_profiles[j]->highway[i]); + printf(" \n"); + + printf(" \n"); + for(i=1;i\n",PropertyName(i),padding+6+strlen(PropertyName(i)),loaded_profiles[j]->props_yes[i]); + printf(" \n"); + + printf(" \n"); + printf(" \n",loaded_profiles[j]->oneway); + printf(" \n",weight_to_tonnes(loaded_profiles[j]->weight)); + printf(" \n",height_to_metres(loaded_profiles[j]->height)); + printf(" \n",width_to_metres(loaded_profiles[j]->width)); + printf(" \n",length_to_metres(loaded_profiles[j]->length)); + printf(" \n"); + + printf(" \n"); + printf("\n"); + } + + printf("\n"); +} + + +/*++++++++++++++++++++++++++++++++++++++ + Print out the profiles as JavaScript Object Notation for use in a web form. + ++++++++++++++++++++++++++++++++++++++*/ + +void PrintProfilesJSON(void) +{ + unsigned int i,j; + + printf("var routino={ // contains all default Routino options (generated using \"--help-profile-json\").\n"); + printf("\n"); + + printf(" // Default transport type\n"); + printf(" transport: 'motorcar',\n"); + printf("\n"); + + printf(" // Transport types\n"); + printf(" transports: {"); + for(j=0;jtransport),j); + printf("},\n"); + printf("\n"); + + printf(" // Highway types\n"); + printf(" highways: {"); + for(i=1;itransport),(int)loaded_profiles[j]->highway[i]); + printf("}%s\n",i==(Way_Count-1)?"":","); + } + printf(" },\n"); + printf("\n"); + + printf(" // Speed limits\n"); + printf(" profile_speed: {\n"); + for(i=1;itransport),loaded_profiles[j]->speed[i]); + printf("}%s\n",i==(Way_Count-1)?"":","); + } + printf(" },\n"); + printf("\n"); + + printf(" // Highway properties\n"); + printf(" profile_property: {\n"); + for(i=1;itransport),(int)loaded_profiles[j]->props_yes[i]); + printf("}%s\n",i==(Property_Count-1)?"":","); + } + printf(" },\n"); + printf("\n"); + + printf(" // Restrictions\n"); + printf(" profile_restrictions: {\n"); + printf(" %12s: {","oneway"); + for(j=0;jtransport),loaded_profiles[j]->oneway); + printf("},\n"); + printf(" %12s: {","weight"); + for(j=0;jtransport),weight_to_tonnes(loaded_profiles[j]->weight)); + printf("},\n"); + printf(" %12s: {","height"); + for(j=0;jtransport),height_to_metres(loaded_profiles[j]->height)); + printf("},\n"); + printf(" %12s: {","width"); + for(j=0;jtransport),width_to_metres(loaded_profiles[j]->width)); + printf("},\n"); + printf(" %12s: {","length"); + for(j=0;jtransport),length_to_metres(loaded_profiles[j]->length)); + printf("}\n"); + printf(" }\n"); + printf("\n"); + + printf("}; // end of routino variable\n"); +} + + +/*++++++++++++++++++++++++++++++++++++++ + Print out the profiles as Perl for use in a web CGI. + ++++++++++++++++++++++++++++++++++++++*/ + +void PrintProfilesPerl(void) +{ + unsigned int i,j; + + printf("$routino={ # contains all default Routino options (generated using \"--help-profile-perl\").\n"); + printf("\n"); + + printf(" # Default transport type\n"); + printf(" transport => 'motorcar',\n"); + printf("\n"); + + printf(" # Transport types\n"); + printf(" transports => {"); + for(j=0;j %d",j==1?"":", ",TransportName(loaded_profiles[j]->transport),j); + printf("},\n"); + printf("\n"); + + printf(" # Highway types\n"); + printf(" highways => {"); + for(i=1;i %d",i==1?"":", ",HighwayName(i),i); + printf("},\n"); + printf("\n"); + + printf(" # Property types\n"); + printf(" properties => {"); + for(i=1;i %d",i==1?"":", ",PropertyName(i),i); + printf("},\n"); + printf("\n"); + + printf(" # Restriction types\n"); + printf(" restrictions => {oneway => 1, weight => 2, height => 3, width => 4, length => 5},\n"); + printf("\n"); + + printf(" # Allowed highways\n"); + printf(" profile_highway => {\n"); + for(i=1;i {",HighwayName(i)); + for(j=0;j %3d",j==1?"":", ",TransportName(loaded_profiles[j]->transport),(int)loaded_profiles[j]->highway[i]); + printf("}%s\n",i==(Way_Count-1)?"":","); + } + printf(" },\n"); + printf("\n"); + + printf(" # Speed limits\n"); + printf(" profile_speed => {\n"); + for(i=1;i {",HighwayName(i)); + for(j=0;j %3d",j==1?"":", ",TransportName(loaded_profiles[j]->transport),loaded_profiles[j]->speed[i]); + printf("}%s\n",i==(Way_Count-1)?"":","); + } + printf(" },\n"); + printf("\n"); + + printf(" # Highway properties\n"); + printf(" profile_property => {\n"); + for(i=1;i {",PropertyName(i)); + for(j=0;j %3d",j==1?"":", ",TransportName(loaded_profiles[j]->transport),(int)loaded_profiles[j]->props_yes[i]); + printf("}%s\n",i==(Property_Count-1)?"":","); + } + printf(" },\n"); + printf("\n"); + + printf(" # Restrictions\n"); + printf(" profile_restrictions => {\n"); + printf(" %12s => {","oneway"); + for(j=0;j %4d",j==1?"":", ",TransportName(loaded_profiles[j]->transport),loaded_profiles[j]->oneway); + printf("},\n"); + printf(" %12s => {","weight"); + for(j=0;j %4.1f",j==1?"":", ",TransportName(loaded_profiles[j]->transport),weight_to_tonnes(loaded_profiles[j]->weight)); + printf("},\n"); + printf(" %12s => {","height"); + for(j=0;j %4.1f",j==1?"":", ",TransportName(loaded_profiles[j]->transport),height_to_metres(loaded_profiles[j]->height)); + printf("},\n"); + printf(" %12s => {","width"); + for(j=0;j %4.1f",j==1?"":", ",TransportName(loaded_profiles[j]->transport),width_to_metres(loaded_profiles[j]->width)); + printf("},\n"); + printf(" %12s => {","length"); + for(j=0;j %4.1f",j==1?"":", ",TransportName(loaded_profiles[j]->transport),length_to_metres(loaded_profiles[j]->length)); + printf("}\n"); + printf(" },\n"); + printf("\n"); + + printf("}; # end of routino variable\n"); +} diff --git a/src/profiles.h b/src/profiles.h new file mode 100644 index 0000000..25a933a --- /dev/null +++ b/src/profiles.h @@ -0,0 +1,79 @@ +/*************************************** + $Header: /home/amb/routino/src/RCS/profiles.h,v 1.16 2010/05/29 10:37:12 amb Exp $ + + A header file for the profiles. + + Part of the Routino routing software. + ******************/ /****************** + This file Copyright 2008-2010 Andrew M. Bishop + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + ***************************************/ + + +#ifndef PROFILES_H +#define PROFILES_H /*+ To stop multiple inclusions. +*/ + +#include "types.h" +#include "ways.h" + + +/* Data structures */ + +/*+ A data structure to hold a transport type profile. +*/ +typedef struct _Profile +{ + char *name; /*+ The name of the profile. +*/ + + Transport transport; /*+ The type of transport. +*/ + + wayallow_t allow; /*+ The type of transport expressed as what must be allowed on a way. +*/ + + score_t highway[Way_Count]; /*+ A floating point preference for travel on the highway. +*/ + score_t max_pref; /*+ The maximum preference for any highway type. +*/ + + speed_t speed[Way_Count]; /*+ The maximum speed on each type of highway. +*/ + speed_t max_speed; /*+ The maximum speed for any highway type. +*/ + + score_t props_yes[Property_Count]; /*+ A floating point preference for ways with this attribute. +*/ + score_t props_no [Property_Count]; /*+ A floating point preference for ways without this attribute. +*/ + + int oneway; /*+ A flag to indicate if one-way restrictions apply. +*/ + + weight_t weight; /*+ The minimum weight of the route. +*/ + + height_t height; /*+ The minimum height of vehicles on the route. +*/ + width_t width; /*+ The minimum width of vehicles on the route. +*/ + length_t length; /*+ The minimum length of vehicles on the route. +*/ +} + Profile; + + +/* Functions */ + +int ParseXMLProfiles(const char *filename); + +Profile *GetProfile(const char *name); + +int UpdateProfile(Profile *profile,Ways *ways); + +void PrintProfile(const Profile *profile); + +void PrintProfilesXML(void); + +void PrintProfilesJSON(void); + +void PrintProfilesPerl(void); + +#endif /* PROFILES_H */ diff --git a/src/queue.c b/src/queue.c new file mode 100644 index 0000000..68c4ccb --- /dev/null +++ b/src/queue.c @@ -0,0 +1,201 @@ +/*************************************** + $Header: /home/amb/routino/src/RCS/queue.c,v 1.7 2009/11/13 19:24:11 amb Exp $ + + Queue data type functions. + + Part of the Routino routing software. + ******************/ /****************** + This file Copyright 2008,2009 Andrew M. Bishop + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + ***************************************/ + + +#include +#include +#include + +#include "results.h" + + +/*+ A queue of results. +*/ +struct _Queue +{ + uint32_t nallocated; /*+ The number of entries allocated. +*/ + uint32_t noccupied; /*+ The number of entries occupied. +*/ + + Result **data; /*+ The queue of pointers to results. +*/ +}; + + +/*++++++++++++++++++++++++++++++++++++++ + Allocate a new queue. + + Queue *NewQueueList Returns the queue. + ++++++++++++++++++++++++++++++++++++++*/ + +Queue *NewQueueList(void) +{ + Queue *queue; + + queue=(Queue*)malloc(sizeof(Queue)); + + queue->nallocated=1023; + queue->noccupied=0; + + queue->data=(Result**)malloc(queue->nallocated*sizeof(Result*)); + + return(queue); +} + + +/*++++++++++++++++++++++++++++++++++++++ + Free a queue. + + Queue *queue The queue to be freed. + ++++++++++++++++++++++++++++++++++++++*/ + +void FreeQueueList(Queue *queue) +{ + free(queue->data); + + free(queue); +} + + +/*++++++++++++++++++++++++++++++++++++++ + Insert a new item into the queue in the right place. + + The data is stored in a "Binary Heap" http://en.wikipedia.org/wiki/Binary_heap + and this operation is adding an item to the heap. + + Queue *queue The queue to insert the result into. + + Result *result The result to insert into the queue. + ++++++++++++++++++++++++++++++++++++++*/ + +void InsertInQueue(Queue *queue,Result *result) +{ + uint32_t index; + + if(result->queued==NOT_QUEUED) + { + if(queue->noccupied==queue->nallocated) + { + queue->nallocated=2*queue->nallocated+1; + queue->data=(Result**)realloc((void*)queue->data,queue->nallocated*sizeof(Result*)); + } + + index=queue->noccupied; + queue->noccupied++; + + queue->data[index]=result; + queue->data[index]->queued=index; + } + else + { + index=result->queued; + } + + /* Bubble up the new value */ + + while(index>0 && + queue->data[index]->sortbydata[(index-1)/2]->sortby) + { + uint32_t newindex; + Result *temp; + + newindex=(index-1)/2; + + temp=queue->data[index]; + queue->data[index]=queue->data[newindex]; + queue->data[newindex]=temp; + + queue->data[index]->queued=index; + queue->data[newindex]->queued=newindex; + + index=newindex; + } +} + + +/*++++++++++++++++++++++++++++++++++++++ + Pop an item from the front of the queue. + + The data is stored in a "Binary Heap" http://en.wikipedia.org/wiki/Binary_heap + and this operation is deleting the root item from the heap. + + Result *PopFromQueue Returns the top item. + + Queue *queue The queue to remove the result from. + ++++++++++++++++++++++++++++++++++++++*/ + +Result *PopFromQueue(Queue *queue) +{ + uint32_t index; + Result *retval; + + if(queue->noccupied==0) + return(NULL); + + retval=queue->data[0]; + retval->queued=NOT_QUEUED; + + index=0; + queue->noccupied--; + + queue->data[index]=queue->data[queue->noccupied]; + + /* Bubble down the newly promoted value */ + + while((2*index+2)noccupied && + (queue->data[index]->sortby>queue->data[2*index+1]->sortby || + queue->data[index]->sortby>queue->data[2*index+2]->sortby)) + { + uint32_t newindex; + Result *temp; + + if(queue->data[2*index+1]->sortbydata[2*index+2]->sortby) + newindex=2*index+1; + else + newindex=2*index+2; + + temp=queue->data[newindex]; + queue->data[newindex]=queue->data[index]; + queue->data[index]=temp; + + queue->data[index]->queued=index; + queue->data[newindex]->queued=newindex; + + index=newindex; + } + + if((2*index+2)==queue->noccupied && + queue->data[index]->sortby>queue->data[2*index+1]->sortby) + { + uint32_t newindex; + Result *temp; + + newindex=2*index+1; + + temp=queue->data[newindex]; + queue->data[newindex]=queue->data[index]; + queue->data[index]=temp; + + queue->data[index]->queued=index; + queue->data[newindex]->queued=newindex; + } + + return(retval); +} diff --git a/src/results.c b/src/results.c new file mode 100644 index 0000000..b5dec42 --- /dev/null +++ b/src/results.c @@ -0,0 +1,242 @@ +/*************************************** + $Header: /home/amb/routino/src/RCS/results.c,v 1.21 2010/07/07 19:04:18 amb Exp $ + + Result data type functions. + + Part of the Routino routing software. + ******************/ /****************** + This file Copyright 2008-2010 Andrew M. Bishop + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + ***************************************/ + + +#include +#include +#include + +#include "results.h" + +/*+ The size of the increment for the Results data structure. +*/ +#define RESULTS_INCREMENT 64 + + +/*++++++++++++++++++++++++++++++++++++++ + Allocate a new results list. + + Results *NewResultsList Returns the results list. + + int nbins The number of bins in the results array. + ++++++++++++++++++++++++++++++++++++++*/ + +Results *NewResultsList(int nbins) +{ + Results *results; + uint32_t i; + + results=(Results*)malloc(sizeof(Results)); + + results->nbins=1; + results->mask=~0; + + while(nbins>>=1) + { + results->mask<<=1; + results->nbins<<=1; + } + + results->mask=~results->mask; + + results->alloced=RESULTS_INCREMENT; + results->number=0; + + results->count=(uint32_t*)malloc(results->nbins*sizeof(uint32_t)); + results->point=(Result***)malloc(results->nbins*sizeof(Result**)); + + for(i=0;inbins;i++) + { + results->count[i]=0; + + results->point[i]=(Result**)malloc(results->alloced*sizeof(Result*)); + } + + results->data=(Result**)malloc(1*sizeof(Result*)); + results->data[0]=(Result*)malloc(results->nbins*RESULTS_INCREMENT*sizeof(Result)); + + results->start=NO_NODE; + results->finish=NO_NODE; + + return(results); +} + + +/*++++++++++++++++++++++++++++++++++++++ + Allocate a new results list. + + Results *results The results list to be destroyed. + ++++++++++++++++++++++++++++++++++++++*/ + +void FreeResultsList(Results *results) +{ + int i,c=(results->number-1)/(results->nbins*RESULTS_INCREMENT); + + for(i=c;i>=0;i--) + free(results->data[i]); + + free(results->data); + + for(i=0;inbins;i++) + free(results->point[i]); + + free(results->point); + + free(results->count); + + free(results); +} + + +/*++++++++++++++++++++++++++++++++++++++ + Insert a new result into the results data structure in the right order. + + Result *InsertResult Returns the result that has been inserted. + + Results *results The results structure to insert into. + + index_t node The node that is to be inserted into the results. + ++++++++++++++++++++++++++++++++++++++*/ + +Result *InsertResult(Results *results,index_t node) +{ + int bin=node&results->mask; + uint32_t i; + + /* Check that the arrays have enough space. */ + + if(results->count[bin]==results->alloced) + { + results->alloced+=RESULTS_INCREMENT; + + for(i=0;inbins;i++) + results->point[i]=(Result**)realloc((void*)results->point[i],results->alloced*sizeof(Result*)); + } + + if(results->number && (results->number%RESULTS_INCREMENT)==0 && (results->number%(RESULTS_INCREMENT*results->nbins))==0) + { + int c=results->number/(results->nbins*RESULTS_INCREMENT); + + results->data=(Result**)realloc((void*)results->data,(c+1)*sizeof(Result*)); + results->data[c]=(Result*)malloc(results->nbins*RESULTS_INCREMENT*sizeof(Result)); + } + + /* Insert the new entry */ + + results->point[bin][results->count[bin]]=&results->data[results->number/(results->nbins*RESULTS_INCREMENT)][results->number%(results->nbins*RESULTS_INCREMENT)]; + + results->number++; + + results->count[bin]++; + + results->point[bin][results->count[bin]-1]->node=node; + results->point[bin][results->count[bin]-1]->queued=NOT_QUEUED; + + return(results->point[bin][results->count[bin]-1]); +} + + +/*++++++++++++++++++++++++++++++++++++++ + Zero the values in a result structure. + + Result *result The result to modify. + ++++++++++++++++++++++++++++++++++++++*/ + +void ZeroResult(Result *result) +{ + result->prev=NO_NODE; + result->next=NO_NODE; + + result->score=0; + result->sortby=0; + + result->segment=NULL; +} + + +/*++++++++++++++++++++++++++++++++++++++ + Find a result; search by node. + + Result *FindResult Returns the result that has been found. + + Results *results The results structure to search. + + index_t node The node that is to be found. + ++++++++++++++++++++++++++++++++++++++*/ + +Result *FindResult(Results *results,index_t node) +{ + int bin=node&results->mask; + int i; + + for(i=results->count[bin]-1;i>=0;i--) + if(results->point[bin][i]->node==node) + return(results->point[bin][i]); + + return(NULL); +} + + +/*++++++++++++++++++++++++++++++++++++++ + Find a result from a set of results. + + Result *FirstResult Returns the first results from a set of results. + + Results *results The set of results. + ++++++++++++++++++++++++++++++++++++++*/ + +Result *FirstResult(Results *results) +{ + return(&results->data[0][0]); +} + + +/*++++++++++++++++++++++++++++++++++++++ + Find a result from a set of results. + + Result *NextResult Returns the next result from a set of results. + + Results *results The set of results. + + Result *result The previous result. + ++++++++++++++++++++++++++++++++++++++*/ + +Result *NextResult(Results *results,Result *result) +{ + int i,j=0,c=(results->number-1)/(results->nbins*RESULTS_INCREMENT); + + for(i=0;i<=c;i++) + { + j=result-results->data[i]; + + if(j>=0 && j<(results->nbins*RESULTS_INCREMENT)) + break; + } + + if(++j>=(results->nbins*RESULTS_INCREMENT)) + {i++;j=0;} + + if((i*(results->nbins*RESULTS_INCREMENT)+j)>=results->number) + return(NULL); + + return(&results->data[i][j]); +} diff --git a/src/results.h b/src/results.h new file mode 100644 index 0000000..a7191f0 --- /dev/null +++ b/src/results.h @@ -0,0 +1,112 @@ +/*************************************** + $Header: /home/amb/routino/src/RCS/results.h,v 1.17 2009/11/13 19:24:11 amb Exp $ + + A header file for the results. + + Part of the Routino routing software. + ******************/ /****************** + This file Copyright 2008,2009 Andrew M. Bishop + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + ***************************************/ + + +#ifndef RESULTS_H +#define RESULTS_H /*+ To stop multiple inclusions. +*/ + +#include + +#include "types.h" + + +/* Constants */ + +/*+ A result is not currently queued. +*/ +#define NOT_QUEUED (uint32_t)(~0) + + +/* Data structures */ + +/*+ The result for a node. +*/ +typedef struct _Result +{ + index_t node; /*+ The node for which this result applies. +*/ + + index_t prev; /*+ The previous node following the best path. +*/ + index_t next; /*+ The next node following the best path. +*/ + + score_t score; /*+ The best actual weighted distance or duration score from the start to the node. +*/ + + score_t sortby; /*+ The best possible weighted distance or duration score from the start to the finish. +*/ + uint32_t queued; /*+ The position of this result in the queue. +*/ + + Segment *segment; /*+ The segment for the path to here (from prev). +*/ +} + Result; + +/*+ A list of results. +*/ +typedef struct _Results +{ + uint32_t nbins; /*+ The number of bins. +*/ + uint32_t mask; /*+ A bit mask to select the bottom 'nbins' bits. +*/ + + uint32_t alloced; /*+ The amount of space allocated for results + (the length of the number and pointers arrays and + 1/nbins times the amount in the real results). +*/ + uint32_t number; /*+ The total number of occupied results. +*/ + + uint32_t *count; /*+ An array of nbins counters of results in each array. +*/ + Result ***point; /*+ An array of nbins arrays of pointers to actual results. +*/ + + Result **data; /*+ An array of arrays containing the actual results + (don't need to realloc the array of data when adding more, + only realloc the array that points to the array of data). + Most importantly pointers into the real data don't change + as more space is allocated (since realloc is not being used). +*/ + + index_t start; /*+ The start node. +*/ + index_t finish; /*+ The finish node. +*/ +} + Results; + + +/* Forward definitions for opaque type */ + +typedef struct _Queue Queue; + + +/* Results Functions */ + +Results *NewResultsList(int nbins); +void FreeResultsList(Results *results); + +Result *InsertResult(Results *results,index_t node); +void ZeroResult(Result *result); + +Result *FindResult(Results *results,index_t node); + +Result *FirstResult(Results *results); +Result *NextResult(Results *results,Result *result); + + +/* Queue Functions */ + +Queue *NewQueueList(void); +void FreeQueueList(Queue *queue); + +void InsertInQueue(Queue *queue,Result *result); +Result *PopFromQueue(Queue *queue); + + +#endif /* RESULTS_H */ diff --git a/src/router.c b/src/router.c new file mode 100644 index 0000000..46ca0b4 --- /dev/null +++ b/src/router.c @@ -0,0 +1,814 @@ +/*************************************** + $Header: /home/amb/routino/src/RCS/router.c,v 1.83 2010/06/28 17:56:26 amb Exp $ + + OSM router. + + Part of the Routino routing software. + ******************/ /****************** + This file Copyright 2008-2010 Andrew M. Bishop + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + ***************************************/ + + +#include +#include +#include +#include + +#include "types.h" +#include "functions.h" +#include "translations.h" +#include "profiles.h" +#include "nodes.h" +#include "segments.h" +#include "ways.h" + + +/*+ The number of waypoints allowed to be specified. +*/ +#define NWAYPOINTS 99 + +/*+ The maximum distance from the specified point to search for a node or segment (in km). +*/ +#define MAXSEARCH 1 + +/*+ The minimum distance along a segment from a node to insert a fake node. (in km). +*/ +#define MINSEGMENT 0.005 + + +/*+ A set of fake segments to allow start/finish in the middle of a segment. +*/ +static Segment fake_segments[2*NWAYPOINTS]; + +/*+ A set of fake node latitudes and longitudes. +*/ +static double point_lon[NWAYPOINTS+1],point_lat[NWAYPOINTS+1]; + +/*+ The option not to print any progress information. +*/ +int option_quiet=0; + +/*+ The options to select the format of the output. +*/ +int option_html=0,option_gpx_track=0,option_gpx_route=0,option_text=0,option_text_all=0,option_none=0; + +/*+ The option to calculate the quickest route insted of the shortest. +*/ +int option_quickest=0; + + +/* Local functions */ + +static void print_usage(int detail); + + +/*++++++++++++++++++++++++++++++++++++++ + The main program for the router. + ++++++++++++++++++++++++++++++++++++++*/ + +int main(int argc,char** argv) +{ + Nodes *OSMNodes; + Segments *OSMSegments; + Ways *OSMWays; + Results *results[NWAYPOINTS+1]={NULL}; + int point_used[NWAYPOINTS+1]={0}; + int help_profile=0,help_profile_xml=0,help_profile_json=0,help_profile_pl=0; + char *dirname=NULL,*prefix=NULL; + char *profiles=NULL,*profilename=NULL; + char *translations=NULL,*language=NULL; + int exactnodes=0; + Transport transport=Transport_None; + Profile *profile=NULL; + index_t start=NO_NODE,finish=NO_NODE; + int arg,point; + + /* Parse the command line arguments */ + + if(argc<2) + print_usage(0); + + /* Get the non-routing, general program options */ + + for(arg=1;argtransport=transport; + } + + /* Parse the other command line arguments */ + + for(arg=1;argNWAYPOINTS || point_used[point]&1) + print_usage(0); + + point_lon[point]=degrees_to_radians(atof(p)); + point_used[point]+=1; + } + else if(!strncmp(argv[arg],"--lat",5) && isdigit(argv[arg][5])) + { + char *p=&argv[arg][6]; + while(isdigit(*p)) p++; + if(*p++!='=') + print_usage(0); + + point=atoi(&argv[arg][5]); + if(point>NWAYPOINTS || point_used[point]&2) + print_usage(0); + + point_lat[point]=degrees_to_radians(atof(p)); + point_used[point]+=2; + } + else if(!strncmp(argv[arg],"--transport=",12)) + ; /* Done this already */ + else if(!strncmp(argv[arg],"--highway-",10)) + { + Highway highway; + char *equal=strchr(argv[arg],'='); + char *string; + + if(!equal) + print_usage(0); + + string=strcpy((char*)malloc(strlen(argv[arg])),argv[arg]+10); + string[equal-argv[arg]-10]=0; + + highway=HighwayType(string); + + if(highway==Way_Count) + print_usage(0); + + profile->highway[highway]=atof(equal+1); + + free(string); + } + else if(!strncmp(argv[arg],"--speed-",8)) + { + Highway highway; + char *equal=strchr(argv[arg],'='); + char *string; + + if(!equal) + print_usage(0); + + string=strcpy((char*)malloc(strlen(argv[arg])),argv[arg]+8); + string[equal-argv[arg]-8]=0; + + highway=HighwayType(string); + + if(highway==Way_Count) + print_usage(0); + + profile->speed[highway]=kph_to_speed(atof(equal+1)); + + free(string); + } + else if(!strncmp(argv[arg],"--property-",11)) + { + Property property; + char *equal=strchr(argv[arg],'='); + char *string; + + if(!equal) + print_usage(0); + + string=strcpy((char*)malloc(strlen(argv[arg])),argv[arg]+11); + string[equal-argv[arg]-11]=0; + + property=PropertyType(string); + + if(property==Way_Count) + print_usage(0); + + profile->props_yes[property]=atof(equal+1); + + free(string); + } + else if(!strncmp(argv[arg],"--oneway=",9)) + profile->oneway=!!atoi(&argv[arg][9]); + else if(!strncmp(argv[arg],"--weight=",9)) + profile->weight=tonnes_to_weight(atof(&argv[arg][9])); + else if(!strncmp(argv[arg],"--height=",9)) + profile->height=metres_to_height(atof(&argv[arg][9])); + else if(!strncmp(argv[arg],"--width=",8)) + profile->width=metres_to_width(atof(&argv[arg][8])); + else if(!strncmp(argv[arg],"--length=",9)) + profile->length=metres_to_length(atof(&argv[arg][9])); + else + print_usage(0); + } + + for(point=1;point<=NWAYPOINTS;point++) + if(point_used[point]==1 || point_used[point]==2) + print_usage(0); + + if(help_profile) + { + PrintProfile(profile); + + return(0); + } + else if(help_profile_xml) + { + PrintProfilesXML(); + + return(0); + } + else if(help_profile_json) + { + PrintProfilesJSON(); + + return(0); + } + else if(help_profile_pl) + { + PrintProfilesPerl(); + + return(0); + } + + /* Load in the translations */ + + if(option_html==0 && option_gpx_track==0 && option_gpx_route==0 && option_text==0 && option_text_all==0 && option_none==0) + option_html=option_gpx_track=option_gpx_route=option_text=option_text_all=1; + + if(option_html || option_gpx_route || option_gpx_track) + { + if(translations && ExistsFile(translations)) + ; + else if(!translations && ExistsFile(FileName(dirname,prefix,"translations.xml"))) + translations=FileName(dirname,prefix,"translations.xml"); + + if(!translations && language) + { + fprintf(stderr,"Error: Cannot use '--language' option without reading some translations.\n"); + return(1); + } + + if(translations && ParseXMLTranslations(translations,language)) + { + fprintf(stderr,"Error: Cannot read the translations in the file '%s'.\n",translations); + return(1); + } + } + + /* Load in the data - Note: No error checking because Load*List() will call exit() in case of an error. */ + + OSMNodes=LoadNodeList(FileName(dirname,prefix,"nodes.mem")); + + OSMSegments=LoadSegmentList(FileName(dirname,prefix,"segments.mem")); + + OSMWays=LoadWayList(FileName(dirname,prefix,"ways.mem")); + + if(UpdateProfile(profile,OSMWays)) + { + fprintf(stderr,"Error: Profile is invalid or not compatible with database.\n"); + return(1); + } + + /* Loop through all pairs of points */ + + for(point=1;point<=NWAYPOINTS;point++) + { + Results *begin,*end; + distance_t distmax=km_to_distance(MAXSEARCH); + distance_t distmin; + Segment *segment=NULL; + index_t node1,node2; + + if(point_used[point]!=3) + continue; + + /* Find the closest point */ + + start=finish; + + if(exactnodes) + { + finish=FindClosestNode(OSMNodes,OSMSegments,OSMWays,point_lat[point],point_lon[point],distmax,profile,&distmin); + } + else + { + distance_t dist1,dist2; + + if((segment=FindClosestSegment(OSMNodes,OSMSegments,OSMWays,point_lat[point],point_lon[point],distmax,profile,&distmin,&node1,&node2,&dist1,&dist2))) + finish=CreateFakes(OSMNodes,point,segment,node1,node2,dist1,dist2); + else + finish=NO_NODE; + } + + if(finish==NO_NODE) + { + fprintf(stderr,"Error: Cannot find node close to specified point %d.\n",point); + return(1); + } + + if(!option_quiet) + { + double lat,lon; + + if(IsFakeNode(finish)) + GetFakeLatLong(finish,&lat,&lon); + else + GetLatLong(OSMNodes,finish,&lat,&lon); + + if(IsFakeNode(finish)) + printf("Point %d is segment %d (node %d -> %d): %3.6f %4.6f = %2.3f km\n",point,IndexSegment(OSMSegments,segment),node1,node2, + radians_to_degrees(lon),radians_to_degrees(lat),distance_to_km(distmin)); + else + printf("Point %d is node %d: %3.6f %4.6f = %2.3f km\n",point,finish, + radians_to_degrees(lon),radians_to_degrees(lat),distance_to_km(distmin)); + } + + if(start==NO_NODE) + continue; + + if(start==finish) + continue; + + /* Calculate the beginning of the route */ + + if(!IsFakeNode(start) && IsSuperNode(OSMNodes,start)) + { + Result *result; + + begin=NewResultsList(1); + + begin->start=start; + + result=InsertResult(begin,start); + + ZeroResult(result); + } + else + { + begin=FindStartRoutes(OSMNodes,OSMSegments,OSMWays,start,profile); + + if(!begin) + { + fprintf(stderr,"Error: Cannot find initial section of route compatible with profile.\n"); + return(1); + } + } + + if(FindResult(begin,finish)) + { + FixForwardRoute(begin,finish); + + results[point]=begin; + + if(!option_quiet) + { + printf("\rRouted: Super-Nodes Checked = %d\n",begin->number); + fflush(stdout); + } + } + else + { + Results *superresults; + + /* Calculate the end of the route */ + + if(!IsFakeNode(finish) && IsSuperNode(OSMNodes,finish)) + { + Result *result; + + end=NewResultsList(1); + + end->finish=finish; + + result=InsertResult(end,finish); + + ZeroResult(result); + } + else + { + end=FindFinishRoutes(OSMNodes,OSMSegments,OSMWays,finish,profile); + + if(!end) + { + fprintf(stderr,"Error: Cannot find final section of route compatible with profile.\n"); + return(1); + } + } + + /* Calculate the middle of the route */ + + superresults=FindMiddleRoute(OSMNodes,OSMSegments,OSMWays,begin,end,profile); + + FreeResultsList(begin); + FreeResultsList(end); + + if(!superresults) + { + fprintf(stderr,"Error: Cannot find route compatible with profile.\n"); + return(1); + } + + results[point]=CombineRoutes(superresults,OSMNodes,OSMSegments,OSMWays,profile); + + FreeResultsList(superresults); + } + } + + /* Print out the combined route */ + + if(!option_none) + PrintRoute(results,NWAYPOINTS,OSMNodes,OSMSegments,OSMWays,profile); + + return(0); +} + + +/*++++++++++++++++++++++++++++++++++++++ + Create a pair of fake segments corresponding to the given segment split in two. + + index_t CreateFakes Returns the fake node index (or a real one in special cases). + + Nodes *nodes The set of nodes to use. + + int point Which of the waypoints is this. + + Segment *segment The segment to split. + + index_t node1 The first node at the end of this segment. + + index_t node2 The second node at the end of this segment. + + distance_t dist1 The distance to the first node. + + distance_t dist2 The distance to the second node. + ++++++++++++++++++++++++++++++++++++++*/ + +index_t CreateFakes(Nodes *nodes,int point,Segment *segment,index_t node1,index_t node2,distance_t dist1,distance_t dist2) +{ + index_t fakenode; + double lat1,lon1,lat2,lon2; + + /* Check if we are actually close enough to an existing node */ + + if(dist1km_to_distance(MINSEGMENT)) + return(node1); + + if(dist2km_to_distance(MINSEGMENT)) + return(node2); + + if(dist13 && lat2<-3) + lat2+=2*M_PI; + else if(lat1<-3 && lat2>3) + lat1+=2*M_PI; + + point_lat[point]=lat1+(lat2-lat1)*(double)dist1/(double)(dist1+dist2); + point_lon[point]=lon1+(lon2-lon1)*(double)dist1/(double)(dist1+dist2); + + if(point_lat[point]>M_PI) point_lat[point]-=2*M_PI; + + /* Create the first fake segment */ + + fake_segments[2*point-2]=*segment; + + if(segment->node1==node1) + fake_segments[2*point-2].node1=fakenode; + else + fake_segments[2*point-2].node2=fakenode; + + fake_segments[2*point-2].distance=DISTANCE(dist1)|DISTFLAG(segment->distance); + + /* Create the second fake segment */ + + fake_segments[2*point-1]=*segment; + + if(segment->node1==node2) + fake_segments[2*point-1].node1=fakenode; + else + fake_segments[2*point-1].node2=fakenode; + + fake_segments[2*point-1].distance=DISTANCE(dist2)|DISTFLAG(segment->distance); + + return(fakenode); +} + + +/*++++++++++++++++++++++++++++++++++++++ + Lookup the latitude and longitude of a fake node. + + index_t fakenode The node to lookup. + + double *latitude Returns the latitude + + double *longitude Returns the longitude. + ++++++++++++++++++++++++++++++++++++++*/ + +void GetFakeLatLong(index_t fakenode, double *latitude,double *longitude) +{ + index_t realnode=fakenode&(~NODE_SUPER); + + *latitude =point_lat[realnode]; + *longitude=point_lon[realnode]; +} + + +/*++++++++++++++++++++++++++++++++++++++ + Finds the first fake segment associated to a fake node. + + Segment *FirstFakeSegment Returns the first fake segment. + + index_t fakenode The node to lookup. + ++++++++++++++++++++++++++++++++++++++*/ + +Segment *FirstFakeSegment(index_t fakenode) +{ + index_t realnode=fakenode&(~NODE_SUPER); + + return(&fake_segments[2*realnode-2]); +} + + +/*++++++++++++++++++++++++++++++++++++++ + Finds the next (there can only be two) fake segment associated to a fake node. + + Segment *NextFakeSegment Returns the second fake segment. + + Segment *segment The first fake segment. + + index_t fakenode The node to lookup. + ++++++++++++++++++++++++++++++++++++++*/ + +Segment *NextFakeSegment(Segment *segment,index_t fakenode) +{ + index_t realnode=fakenode&(~NODE_SUPER); + + if(segment==&fake_segments[2*realnode-2]) + return(&fake_segments[2*realnode-1]); + else + return(NULL); +} + + +/*++++++++++++++++++++++++++++++++++++++ + Finds the next (there can only be two) fake segment associated to a fake node. + + Segment *ExtraFakeSegment Returns a segment between the two specified nodes if it exists. + + index_t node The real node. + + index_t fakenode The fake node to lookup. + ++++++++++++++++++++++++++++++++++++++*/ + +Segment *ExtraFakeSegment(index_t node,index_t fakenode) +{ + index_t realnode=fakenode&(~NODE_SUPER); + + if(fake_segments[2*realnode-2].node1==node || fake_segments[2*realnode-2].node2==node) + return(&fake_segments[2*realnode-2]); + + if(fake_segments[2*realnode-1].node1==node || fake_segments[2*realnode-1].node2==node) + return(&fake_segments[2*realnode-1]); + + return(NULL); +} + + +/*++++++++++++++++++++++++++++++++++++++ + Print out the usage information. + + int detail The level of detail to use - 0 = low, 1 = high. + ++++++++++++++++++++++++++++++++++++++*/ + +static void print_usage(int detail) +{ + fprintf(stderr, + "Usage: router [--help | --help-profile | --help-profile-xml |\n" + " --help-profile-json | --help-profile-perl ]\n" + " [--dir=] [--prefix=]\n" + " [--profiles=] [--translations=]\n" + " [--exact-nodes-only]\n" + " [--quiet]\n" + " [--language=]\n" + " [--output-html]\n" + " [--output-gpx-track] [--output-gpx-route]\n" + " [--output-text] [--output-text-all]\n" + " [--output-none]\n" + " [--profile=]\n" + " [--transport=]\n" + " [--shortest | --quickest]\n" + " --lon1= --lat1=\n" + " --lon2= --lon2=\n" + " [ ... --lon99= --lon99=]\n" + " [--highway-= ...]\n" + " [--speed-= ...]\n" + " [--property-= ...]\n" + " [--oneway=(0|1)]\n" + " [--weight=]\n" + " [--height=] [--width=] [--length=]\n"); + + if(detail) + fprintf(stderr, + "\n" + "--help Prints this information.\n" + "--help-profile Prints the information about the selected profile.\n" + "--help-profile-xml Prints all loaded profiles in XML format.\n" + "--help-profile-json Prints all loaded profiles in JSON format.\n" + "--help-profile-perl Prints all loaded profiles in Perl format.\n" + "\n" + "--dir= The directory containing the routing database.\n" + "--prefix= The filename prefix for the routing database.\n" + "--profiles= The name of the profiles (defaults to 'profiles.xml'\n" + " with '--dirname' and '--prefix' options).\n" + "--translations= The filename of the translations (defaults to\n" + " 'translations.xml' with '--dirname' and '--prefix').\n" + "\n" + "--exact-nodes-only Only route between nodes (don't find closest segment).\n" + "\n" + "--quiet Don't print any screen output when running.\n" + "--language= Use the translations for specified language.\n" + "--output-html Write an HTML description of the route.\n" + "--output-gpx-track Write a GPX track file with all route points.\n" + "--output-gpx-route Write a GPX route file with interesting junctions.\n" + "--output-text Write a plain text file with interesting junctions.\n" + "--output-text-all Write a plain test file with all route points.\n" + "--output-none Don't write any output files or read any translations.\n" + " (If no output option is given then all are written.)\n" + "\n" + "--profile= Select the loaded profile with this name.\n" + "--transport= Select the transport to use (selects the profile\n" + " named after the transport if '--profile' is not used.)\n" + "\n" + "--shortest Find the shortest route between the waypoints.\n" + "--quickest Find the quickest route between the waypoints.\n" + "\n" + "--lon= Specify the longitude of the n'th waypoint.\n" + "--lat= Specify the latitude of the n'th waypoint.\n" + "\n" + " Routing preference options\n" + "--highway-= * preference for highway type (%%).\n" + "--speed-= * speed for highway type (km/h).\n" + "--property-= * preference for proprty type (%%).\n" + "--oneway=(0|1) * oneway streets are to be obeyed.\n" + "--weight= * maximum weight limit (tonnes).\n" + "--height= * maximum height limit (metres).\n" + "--width= * maximum width limit (metres).\n" + "--length= * maximum length limit (metres).\n" + "\n" + " defaults to motorcar but can be set to:\n" + "%s" + "\n" + " can be selected from:\n" + "%s" + "\n" + " can be selected from:\n" + "%s", + TransportList(),HighwayList(),PropertyList()); + + exit(!detail); +} diff --git a/src/segments.c b/src/segments.c new file mode 100644 index 0000000..0557d50 --- /dev/null +++ b/src/segments.c @@ -0,0 +1,170 @@ +/*************************************** + $Header: /home/amb/routino/src/RCS/segments.c,v 1.45 2010/04/28 17:27:02 amb Exp $ + + Segment data type functions. + + Part of the Routino routing software. + ******************/ /****************** + This file Copyright 2008-2010 Andrew M. Bishop + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + ***************************************/ + + +#include +#include + +#include "types.h" +#include "functions.h" +#include "nodes.h" +#include "segments.h" +#include "ways.h" +#include "profiles.h" + + +/*++++++++++++++++++++++++++++++++++++++ + Load in a segment list from a file. + + Segments* LoadSegmentList Returns the segment list that has just been loaded. + + const char *filename The name of the file to load. + ++++++++++++++++++++++++++++++++++++++*/ + +Segments *LoadSegmentList(const char *filename) +{ + void *data; + Segments *segments; + + segments=(Segments*)malloc(sizeof(Segments)); + + data=MapFile(filename); + + /* Copy the Segments structure from the loaded data */ + + *segments=*((Segments*)data); + + /* Adjust the pointers in the Segments structure. */ + + segments->data=data; + segments->segments=(Segment*)(data+sizeof(Segments)); + + return(segments); +} + + +/*++++++++++++++++++++++++++++++++++++++ + Find the next segment with a particular starting node. + + Segment *NextSegment Returns a pointer to the next segment with the same id. + + Segments* segments The set of segments to process. + + Segment *segment The current segment. + + index_t node The current node. + ++++++++++++++++++++++++++++++++++++++*/ + +Segment *NextSegment(Segments* segments,Segment *segment,index_t node) +{ + if(segment->node1==node) + { + segment++; + if((segment-segments->segments)>=segments->number || segment->node1!=node) + return(NULL); + else + return(segment); + } + else + { + if(segment->next2==NO_NODE) + return(NULL); + else + return(LookupSegment(segments,segment->next2)); + } +} + + +/*++++++++++++++++++++++++++++++++++++++ + Calculate the distance between two locations. + + distance_t Distance Returns the distance between the locations. + + double lat1 The latitude of the first location. + + double lon1 The longitude of the first location. + + double lat2 The latitude of the second location. + + double lon2 The longitude of the second location. + ++++++++++++++++++++++++++++++++++++++*/ + +distance_t Distance(double lat1,double lon1,double lat2,double lon2) +{ + double dlon = lon1 - lon2; + double dlat = lat1 - lat2; + + double a1,a2,a,sa,c,d; + + if(dlon==0 && dlat==0) + return 0; + + a1 = sin (dlat / 2); + a2 = sin (dlon / 2); + a = (a1 * a1) + cos (lat1) * cos (lat2) * a2 * a2; + sa = sqrt (a); + if (sa <= 1.0) + {c = 2 * asin (sa);} + else + {c = 2 * asin (1.0);} + d = 6378.137 * c; + + return km_to_distance(d); +} + + +/*++++++++++++++++++++++++++++++++++++++ + Calculate the duration of segment. + + duration_t Duration Returns the duration of travel between the nodes. + + Segment *segment The segment to traverse. + + Way *way The way that the segment belongs to. + + Profile *profile The profile of the transport being used. + ++++++++++++++++++++++++++++++++++++++*/ + +duration_t Duration(Segment *segment,Way *way,Profile *profile) +{ + speed_t speed1=way->speed; + speed_t speed2=profile->speed[HIGHWAY(way->type)]; + distance_t distance=DISTANCE(segment->distance); + + if(speed1==0) + { + if(speed2==0) + return(hours_to_duration(10)); + else + return distance_speed_to_duration(distance,speed2); + } + else /* if(speed1!=0) */ + { + if(speed2==0) + return distance_speed_to_duration(distance,speed1); + else if(speed1<=speed2) + return distance_speed_to_duration(distance,speed1); + else + return distance_speed_to_duration(distance,speed2); + } +} diff --git a/src/segments.h b/src/segments.h new file mode 100644 index 0000000..8b6554f --- /dev/null +++ b/src/segments.h @@ -0,0 +1,101 @@ +/*************************************** + $Header: /home/amb/routino/src/RCS/segments.h,v 1.34 2009/11/14 19:39:20 amb Exp $ + + A header file for the segments. + + Part of the Routino routing software. + ******************/ /****************** + This file Copyright 2008,2009 Andrew M. Bishop + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + ***************************************/ + + +#ifndef SEGMENTS_H +#define SEGMENTS_H /*+ To stop multiple inclusions. +*/ + +#include + +#include "types.h" +#include "profiles.h" + + +/* Data structures */ + + +/*+ A structure containing a single segment. +*/ +struct _Segment +{ + index_t node1; /*+ The index of the starting node. +*/ + index_t node2; /*+ The index of the finishing node. +*/ + + index_t next2; /*+ The index of the next segment sharing node2. +*/ + + index_t way; /*+ The index of the way associated with the segment. +*/ + + distance_t distance; /*+ The distance between the nodes. +*/ +}; + + +/*+ A structure containing a set of segments (mmap format). +*/ +struct _Segments +{ + uint32_t number; /*+ How many segments in total? +*/ + uint32_t snumber; /*+ How many super-segments? +*/ + uint32_t nnumber; /*+ How many normal segments? +*/ + + Segment *segments; /*+ An array of segments. +*/ + + void *data; /*+ The memory mapped data. +*/ +}; + + +/* Macros */ + + +/*+ Return a segment pointer given a set of segments and an index. +*/ +#define LookupSegment(xxx,yyy) (&(xxx)->segments[yyy]) + +/*+ Return a segment index given a set of segments and a pointer. +*/ +#define IndexSegment(xxx,yyy) ((yyy)-&(xxx)->segments[0]) + +/*+ Return true if this is a normal segment. +*/ +#define IsNormalSegment(xxx) (((xxx)->distance)&SEGMENT_NORMAL) + +/*+ Return true if this is a super-segment. +*/ +#define IsSuperSegment(xxx) (((xxx)->distance)&SEGMENT_SUPER) + +/*+ Return true if the segment is oneway towards the specified node. +*/ +#define IsOnewayTo(xxx,yyy) ((xxx)->node1==(yyy)?((xxx)->distance&ONEWAY_2TO1):((xxx)->distance&ONEWAY_1TO2)) + +/*+ Return true if the segment is oneway from the specified node. +*/ +#define IsOnewayFrom(xxx,yyy) ((xxx)->node2==(yyy)?((xxx)->distance&ONEWAY_2TO1):((xxx)->distance&ONEWAY_1TO2)) + +/*+ Return the other node in the segment that is not the specified node. +*/ +#define OtherNode(xxx,yyy) ((xxx)->node1==(yyy)?(xxx)->node2:(xxx)->node1) + + +/* Functions */ + + +Segments *LoadSegmentList(const char *filename); + +Segment *NextSegment(Segments* segments,Segment *segment,index_t node); + +distance_t Distance(double lat1,double lon1,double lat2,double lon2); + +duration_t Duration(Segment *segment,Way *way,Profile *profile); + + +#endif /* SEGMENTS_H */ diff --git a/src/segmentsx.c b/src/segmentsx.c new file mode 100644 index 0000000..5a4f03b --- /dev/null +++ b/src/segmentsx.c @@ -0,0 +1,1053 @@ +/*************************************** + $Header: /home/amb/routino/src/RCS/segmentsx.c,v 1.51 2010/04/28 17:27:02 amb Exp $ + + Extended Segment data type functions. + + Part of the Routino routing software. + ******************/ /****************** + This file Copyright 2008-2010 Andrew M. Bishop + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + ***************************************/ + + +#include +#include +#include +#include +#include +#include + +#include "types.h" +#include "functions.h" +#include "nodesx.h" +#include "segmentsx.h" +#include "waysx.h" +#include "nodes.h" +#include "segments.h" +#include "ways.h" + + +/* Variables */ + +/*+ The command line '--slim' option. +*/ +extern int option_slim; + +/*+ The command line '--tmpdir' option or its default value. +*/ +extern char *option_tmpdirname; + +/* Local Functions */ + +static int sort_by_id(SegmentX *a,SegmentX *b); + +static distance_t DistanceX(NodeX *nodex1,NodeX *nodex2); + + +/*++++++++++++++++++++++++++++++++++++++ + Allocate a new segment list (create a new file or open an existing one). + + SegmentsX *NewSegmentList Returns the segment list. + + int append Set to 1 if the file is to be opened for appending (now or later). + ++++++++++++++++++++++++++++++++++++++*/ + +SegmentsX *NewSegmentList(int append) +{ + SegmentsX *segmentsx; + + segmentsx=(SegmentsX*)calloc(1,sizeof(SegmentsX)); + + assert(segmentsx); /* Check calloc() worked */ + + segmentsx->filename=(char*)malloc(strlen(option_tmpdirname)+32); + + if(append) + sprintf(segmentsx->filename,"%s/segments.input.tmp",option_tmpdirname); + else + sprintf(segmentsx->filename,"%s/segments.%p.tmp",option_tmpdirname,segmentsx); + + if(append) + { + off_t size; + + segmentsx->fd=AppendFile(segmentsx->filename); + + size=SizeFile(segmentsx->filename); + + segmentsx->xnumber=size/sizeof(SegmentX); + } + else + segmentsx->fd=OpenFile(segmentsx->filename); + + return(segmentsx); +} + + +/*++++++++++++++++++++++++++++++++++++++ + Free a segment list. + + SegmentsX *segmentsx The list to be freed. + + int keep Set to 1 if the file is to be kept. + ++++++++++++++++++++++++++++++++++++++*/ + +void FreeSegmentList(SegmentsX *segmentsx,int keep) +{ + if(!keep) + DeleteFile(segmentsx->filename); + + free(segmentsx->filename); + + if(segmentsx->idata) + free(segmentsx->idata); + + if(segmentsx->firstnode) + free(segmentsx->firstnode); + + if(segmentsx->sdata) + free(segmentsx->sdata); + + free(segmentsx); +} + + +/*++++++++++++++++++++++++++++++++++++++ + Append a single segment to a segment list. + + SegmentsX* segmentsx The set of segments to process. + + way_t way The way that the segment belongs to. + + node_t node1 The first node in the segment. + + node_t node2 The second node in the segment. + + distance_t distance The distance between the nodes (or just the flags). + ++++++++++++++++++++++++++++++++++++++*/ + +void AppendSegment(SegmentsX* segmentsx,way_t way,node_t node1,node_t node2,distance_t distance) +{ + SegmentX segmentx; + + assert(!segmentsx->idata); /* Must not have idata filled in => unsorted */ + + segmentx.node1=node1; + segmentx.node2=node2; + segmentx.way=way; + segmentx.distance=distance; + + WriteFile(segmentsx->fd,&segmentx,sizeof(SegmentX)); + + segmentsx->xnumber++; +} + + +/*++++++++++++++++++++++++++++++++++++++ + Sort the segment list. + + SegmentsX* segmentsx The set of segments to process. + ++++++++++++++++++++++++++++++++++++++*/ + +void SortSegmentList(SegmentsX* segmentsx) +{ + int fd; + + /* Check the start conditions */ + + assert(!segmentsx->idata); /* Must not have idata filled in => unsorted */ + + /* Print the start message */ + + printf("Sorting Segments"); + fflush(stdout); + + /* Close the files and re-open them (finished appending) */ + + CloseFile(segmentsx->fd); + segmentsx->fd=ReOpenFile(segmentsx->filename); + + DeleteFile(segmentsx->filename); + + fd=OpenFile(segmentsx->filename); + + /* Sort by node indexes */ + + filesort_fixed(segmentsx->fd,fd,sizeof(SegmentX),(int (*)(const void*,const void*))sort_by_id,NULL); + + segmentsx->number=segmentsx->xnumber; + + /* Close the files and re-open them */ + + CloseFile(segmentsx->fd); + CloseFile(fd); + + segmentsx->fd=ReOpenFile(segmentsx->filename); + + /* Print the final message */ + + printf("\rSorted Segments: Segments=%d\n",segmentsx->xnumber); + fflush(stdout); +} + + +/*++++++++++++++++++++++++++++++++++++++ + Sort the segments into id order (node1 then node2). + + int sort_by_id Returns the comparison of the node fields. + + SegmentX *a The first segment. + + SegmentX *b The second segment. + ++++++++++++++++++++++++++++++++++++++*/ + +static int sort_by_id(SegmentX *a,SegmentX *b) +{ + node_t a_id1=a->node1; + node_t b_id1=b->node1; + + if(a_id1b_id1) + return(1); + else /* if(a_id1==b_id1) */ + { + node_t a_id2=a->node2; + node_t b_id2=b->node2; + + if(a_id2b_id2) + return(1); + else + { + distance_t a_distance=a->distance; + distance_t b_distance=b->distance; + + if(a_distanceb_distance) + return(1); + else + return(0); + } + } +} + + +/*++++++++++++++++++++++++++++++++++++++ + Find the first segment index with a particular starting node. + + index_t IndexFirstSegmentX Returns a pointer to the index of the first extended segment with the specified id. + + SegmentsX* segmentsx The set of segments to process. + + node_t node The node to look for. + ++++++++++++++++++++++++++++++++++++++*/ + +index_t IndexFirstSegmentX(SegmentsX* segmentsx,node_t node) +{ + int start=0; + int end=segmentsx->number-1; + int mid; + int found; + + /* Check if the first node index exists */ + + if(segmentsx->firstnode) + { + index_t index=segmentsx->firstnode[node]; + + if(segmentsx->firstnode[node+1]==index) + return(NO_SEGMENT); + + return(index); + } + + assert(segmentsx->idata); /* Must have idata filled in => sorted by node 1 */ + + /* Binary search - search key exact match only is required. + * + * # <- start | Check mid and move start or end if it doesn't match + * # | + * # | Since an exact match is wanted we can set end=mid-1 + * # <- mid | or start=mid+1 because we know that mid doesn't match. + * # | + * # | Eventually either end=start or end=start+1 and one of + * # <- end | start or end is the wanted one. + */ + + if(endidata[start]) /* Check key is not before start */ + return(NO_SEGMENT); + else if(node>segmentsx->idata[end]) /* Check key is not after end */ + return(NO_SEGMENT); + else + { + do + { + mid=(start+end)/2; /* Choose mid point */ + + if(segmentsx->idata[mid]idata[mid]>node) /* Mid point is too high */ + end=mid; + else /* Mid point is correct */ + {found=mid; goto found;} + } + while((end-start)>1); + + if(segmentsx->idata[start]==node) /* Start is correct */ + {found=start; goto found;} + + if(segmentsx->idata[end]==node) /* End is correct */ + {found=end; goto found;} + } + + return(NO_SEGMENT); + + found: + + while(found>0 && segmentsx->idata[found-1]==node) + found--; + + return(found); +} + + +/*++++++++++++++++++++++++++++++++++++++ + Find the next segment index with a particular starting node. + + index_t IndexNextSegmentX Returns the index of the next segment with the same id. + + SegmentsX* segmentsx The set of segments to process. + + index_t segindex The current segment index. + + index_t nodeindex The node index. + ++++++++++++++++++++++++++++++++++++++*/ + +index_t IndexNextSegmentX(SegmentsX* segmentsx,index_t segindex,index_t nodeindex) +{ + assert(segmentsx->firstnode); /* Must have firstnode filled in => segments updated */ + + if(++segindex==segmentsx->firstnode[nodeindex+1]) + return(NO_SEGMENT); + else + return(segindex); +} + + +/*++++++++++++++++++++++++++++++++++++++ + Lookup a particular segment. + + SegmentX *LookupSegmentX Returns a pointer to the extended segment with the specified id. + + SegmentsX* segmentsx The set of segments to process. + + index_t index The segment index to look for. + + int position The position in the cache to use. + ++++++++++++++++++++++++++++++++++++++*/ + +SegmentX *LookupSegmentX(SegmentsX* segmentsx,index_t index,int position) +{ + assert(index!=NO_SEGMENT); /* Must be a valid segment */ + + if(option_slim) + { + SeekFile(segmentsx->fd,index*sizeof(SegmentX)); + + ReadFile(segmentsx->fd,&segmentsx->cached[position-1],sizeof(SegmentX)); + + return(&segmentsx->cached[position-1]); + } + else + { + return(&segmentsx->xdata[index]); + } +} + + +/*++++++++++++++++++++++++++++++++++++++ + Remove bad segments (duplicated, zero length or missing nodes). + + NodesX *nodesx The nodes to check. + + SegmentsX *segmentsx The segments to modify. + ++++++++++++++++++++++++++++++++++++++*/ + +void RemoveBadSegments(NodesX *nodesx,SegmentsX *segmentsx) +{ + int duplicate=0,loop=0,missing=0,good=0,total=0; + SegmentX segmentx; + int fd; + node_t prevnode1=NO_NODE,prevnode2=NO_NODE; + + /* Print the start message */ + + printf("Checking: Segments=0 Duplicate=0 Loop=0 Missing-Node=0"); + fflush(stdout); + + /* Allocate the array of indexes */ + + segmentsx->idata=(node_t*)malloc(segmentsx->xnumber*sizeof(node_t)); + + assert(segmentsx->idata); /* Check malloc() worked */ + + /* Modify the on-disk image */ + + DeleteFile(segmentsx->filename); + + fd=OpenFile(segmentsx->filename); + SeekFile(segmentsx->fd,0); + + while(!ReadFile(segmentsx->fd,&segmentx,sizeof(SegmentX))) + { + if(prevnode1==segmentx.node1 && prevnode2==segmentx.node2) + duplicate++; + else if(segmentx.node1==segmentx.node2) + loop++; + else if(IndexNodeX(nodesx,segmentx.node1)==NO_NODE || + IndexNodeX(nodesx,segmentx.node2)==NO_NODE) + missing++; + else + { + WriteFile(fd,&segmentx,sizeof(SegmentX)); + + segmentsx->idata[good]=segmentx.node1; + good++; + + prevnode1=segmentx.node1; + prevnode2=segmentx.node2; + } + + total++; + + if(!(total%10000)) + { + printf("\rChecking: Segments=%d Duplicate=%d Loop=%d Missing-Node=%d",total,duplicate,loop,missing); + fflush(stdout); + } + } + + /* Close the files and re-open them */ + + CloseFile(segmentsx->fd); + CloseFile(fd); + + segmentsx->fd=ReOpenFile(segmentsx->filename); + + segmentsx->number=good; + + /* Print the final message */ + + printf("\rChecked: Segments=%d Duplicate=%d Loop=%d Missing-Node=%d \n",total,duplicate,loop,missing); + fflush(stdout); +} + + +/*++++++++++++++++++++++++++++++++++++++ + Measure the segments and replace node/way ids with indexes. + + SegmentsX* segmentsx The set of segments to process. + + NodesX *nodesx The list of nodes to use. + + WaysX *waysx The list of ways to use. + ++++++++++++++++++++++++++++++++++++++*/ + +void UpdateSegments(SegmentsX* segmentsx,NodesX *nodesx,WaysX *waysx) +{ + index_t index=0; + int i,fd; + SegmentX segmentx; + + /* Print the start message */ + + printf("Measuring Segments: Segments=0"); + fflush(stdout); + + /* Map into memory */ + + if(!option_slim) + nodesx->xdata=MapFile(nodesx->filename); + + /* Free the now-unneeded index */ + + free(segmentsx->idata); + segmentsx->idata=NULL; + + /* Allocate the array of indexes */ + + segmentsx->firstnode=(index_t*)malloc((nodesx->number+1)*sizeof(index_t)); + + assert(segmentsx->firstnode); /* Check malloc() worked */ + + for(i=0;inumber;i++) + segmentsx->firstnode[i]=NO_SEGMENT; + + segmentsx->firstnode[nodesx->number]=segmentsx->number; + + /* Modify the on-disk image */ + + DeleteFile(segmentsx->filename); + + fd=OpenFile(segmentsx->filename); + SeekFile(segmentsx->fd,0); + + while(!ReadFile(segmentsx->fd,&segmentx,sizeof(SegmentX))) + { + index_t node1=IndexNodeX(nodesx,segmentx.node1); + index_t node2=IndexNodeX(nodesx,segmentx.node2); + index_t way =IndexWayX (waysx ,segmentx.way); + + NodeX *nodex1=LookupNodeX(nodesx,node1,1); + NodeX *nodex2=LookupNodeX(nodesx,node2,2); + + /* Replace the node and way ids with their indexes */ + + segmentx.node1=node1; + segmentx.node2=node2; + segmentx.way =way; + + /* Set the distance but preserve the ONEWAY_* flags */ + + segmentx.distance|=DISTANCE(DistanceX(nodex1,nodex2)); + + /* Set the first segment index in the nodes */ + + if(indexfirstnode[node1]) + segmentsx->firstnode[node1]=index; + + /* Write the modified segment */ + + WriteFile(fd,&segmentx,sizeof(SegmentX)); + + index++; + + if(!(index%10000)) + { + printf("\rMeasuring Segments: Segments=%d",index); + fflush(stdout); + } + } + + /* Close the files and re-open them */ + + CloseFile(segmentsx->fd); + CloseFile(fd); + + segmentsx->fd=ReOpenFile(segmentsx->filename); + + /* Free the other now-unneeded indexes */ + + free(nodesx->idata); + nodesx->idata=NULL; + + free(waysx->idata); + waysx->idata=NULL; + + /* Unmap from memory */ + + if(!option_slim) + nodesx->xdata=UnmapFile(nodesx->filename); + + /* Print the final message */ + + printf("\rMeasured Segments: Segments=%d \n",segmentsx->number); + fflush(stdout); +} + + +/*++++++++++++++++++++++++++++++++++++++ + Make the segments all point the same way (node1idata); /* Must not have idata filled in => not sorted by node 1 */ + + /* Print the start message */ + + printf("Rotating Segments: Segments=0 Rotated=0"); + fflush(stdout); + + /* Close the files and re-open them (finished appending) */ + + CloseFile(segmentsx->fd); + segmentsx->fd=ReOpenFile(segmentsx->filename); + + DeleteFile(segmentsx->filename); + + fd=OpenFile(segmentsx->filename); + + /* Modify the file contents */ + + while(!ReadFile(segmentsx->fd,&segmentx,sizeof(SegmentX))) + { + if(segmentx.node1>segmentx.node2) + { + node_t temp; + + temp=segmentx.node1; + segmentx.node1=segmentx.node2; + segmentx.node2=temp; + + if(segmentx.distance&(ONEWAY_2TO1|ONEWAY_1TO2)) + segmentx.distance^=ONEWAY_2TO1|ONEWAY_1TO2; + + rotated++; + } + + WriteFile(fd,&segmentx,sizeof(SegmentX)); + + index++; + + if(!(index%10000)) + { + printf("\rRotating Segments: Segments=%d Rotated=%d",index,rotated); + fflush(stdout); + } + } + + /* Close the files and re-open them */ + + CloseFile(segmentsx->fd); + CloseFile(fd); + + segmentsx->fd=ReOpenFile(segmentsx->filename); + + /* Print the final message */ + + printf("\rRotated Segments: Segments=%d Rotated=%d \n",index,rotated); + fflush(stdout); +} + + +/*++++++++++++++++++++++++++++++++++++++ + Remove the duplicate segments. + + SegmentsX* segmentsx The set of segments to process. + + NodesX *nodesx The list of nodes to use. + + WaysX *waysx The list of ways to use. + ++++++++++++++++++++++++++++++++++++++*/ + +void DeduplicateSegments(SegmentsX* segmentsx,NodesX *nodesx,WaysX *waysx) +{ + int duplicate=0,good=0; + index_t firstindex=0,index=0; + int i,fd; + SegmentX prevsegmentx[16],segmentx; + + /* Print the start message */ + + printf("Deduplicating Segments: Segments=0 Duplicate=0"); + fflush(stdout); + + /* Map into memory */ + + if(!option_slim) + waysx->xdata=MapFile(waysx->filename); + + /* Allocate the array of indexes */ + + segmentsx->firstnode=(index_t*)malloc((nodesx->number+1)*sizeof(index_t)); + + assert(segmentsx->firstnode); /* Check malloc() worked */ + + for(i=0;inumber;i++) + segmentsx->firstnode[i]=NO_SEGMENT; + + segmentsx->firstnode[nodesx->number]=segmentsx->number; + + /* Modify the on-disk image */ + + DeleteFile(segmentsx->filename); + + fd=OpenFile(segmentsx->filename); + SeekFile(segmentsx->fd,0); + + while(!ReadFile(segmentsx->fd,&segmentx,sizeof(SegmentX))) + { + int isduplicate=0; + + if(index && segmentx.node1==prevsegmentx[0].node1 && + segmentx.node2==prevsegmentx[0].node2) + { + index_t previndex=firstindex; + + while(previndexway,&wayx2->way)) + { + isduplicate=1; + duplicate++; + break; + } + } + + previndex++; + } + + assert((index-firstindex)<(sizeof(prevsegmentx)/sizeof(prevsegmentx[0]))); + + prevsegmentx[index-firstindex]=segmentx; + } + else + { + firstindex=index; + prevsegmentx[0]=segmentx; + } + + if(!isduplicate) + { + WriteFile(fd,&segmentx,sizeof(SegmentX)); + + if(goodfirstnode[segmentx.node1]) + segmentsx->firstnode[segmentx.node1]=good; + + good++; + } + + index++; + + if(!(index%10000)) + { + printf("\rDeduplicating Segments: Segments=%d Duplicate=%d",index,duplicate); + fflush(stdout); + } + } + + /* Close the files and re-open them */ + + CloseFile(segmentsx->fd); + CloseFile(fd); + + segmentsx->fd=ReOpenFile(segmentsx->filename); + + segmentsx->number=good; + + /* Fix-up the firstnode index for the missing nodes */ + + for(i=nodesx->number-1;i>=0;i--) + if(segmentsx->firstnode[i]==NO_SEGMENT) + segmentsx->firstnode[i]=segmentsx->firstnode[i+1]; + + /* Unmap from memory */ + + if(!option_slim) + waysx->xdata=UnmapFile(waysx->filename); + + /* Print the final message */ + + printf("\rDeduplicated Segments: Segments=%d Duplicate=%d Unique=%d\n",index,duplicate,index-duplicate); + fflush(stdout); +} + + +/*++++++++++++++++++++++++++++++++++++++ + Create the real segments data. + + SegmentsX* segmentsx The set of segments to use. + + WaysX* waysx The set of ways to use. + ++++++++++++++++++++++++++++++++++++++*/ + +void CreateRealSegments(SegmentsX *segmentsx,WaysX *waysx) +{ + index_t i; + + /* Check the start conditions */ + + assert(!segmentsx->sdata); /* Must not have sdata filled in => no real segments */ + + /* Print the start message */ + + printf("Creating Real Segments: Segments=0"); + fflush(stdout); + + /* Map into memory */ + + if(!option_slim) + { + segmentsx->xdata=MapFile(segmentsx->filename); + waysx->xdata=MapFile(waysx->filename); + } + + /* Free the unneeded memory */ + + free(segmentsx->firstnode); + segmentsx->firstnode=NULL; + + /* Allocate the memory */ + + segmentsx->sdata=(Segment*)malloc(segmentsx->number*sizeof(Segment)); + + assert(segmentsx->sdata); /* Check malloc() worked */ + + /* Loop through and fill */ + + for(i=0;inumber;i++) + { + SegmentX *segmentx=LookupSegmentX(segmentsx,i,1); + WayX *wayx=LookupWayX(waysx,segmentx->way,1); + + segmentsx->sdata[i].node1=0; + segmentsx->sdata[i].node2=0; + segmentsx->sdata[i].next2=NO_NODE; + segmentsx->sdata[i].way=wayx->prop; + segmentsx->sdata[i].distance=segmentx->distance; + + if(!((i+1)%10000)) + { + printf("\rCreating Real Segments: Segments=%d",i+1); + fflush(stdout); + } + } + + /* Unmap from memory */ + + if(!option_slim) + { + segmentsx->xdata=UnmapFile(segmentsx->filename); + waysx->xdata=UnmapFile(waysx->filename); + } + + /* Print the final message */ + + printf("\rCreating Real Segments: Segments=%d \n",segmentsx->number); + fflush(stdout); +} + + +/*++++++++++++++++++++++++++++++++++++++ + Assign the nodes indexes to the segments. + + SegmentsX* segmentsx The set of segments to process. + + NodesX *nodesx The list of nodes to use. + ++++++++++++++++++++++++++++++++++++++*/ + +void IndexSegments(SegmentsX* segmentsx,NodesX *nodesx) +{ + index_t i; + + /* Check the start conditions */ + + assert(nodesx->ndata); /* Must have ndata filled in => real nodes exist */ + assert(segmentsx->sdata); /* Must have sdata filled in => real segments exist */ + + /* Print the start message */ + + printf("Indexing Nodes: Nodes=0"); + fflush(stdout); + + /* Map into memory */ + + if(!option_slim) + { + nodesx->xdata=MapFile(nodesx->filename); + segmentsx->xdata=MapFile(segmentsx->filename); + } + + /* Index the segments */ + + for(i=0;inumber;i++) + { + NodeX *nodex=LookupNodeX(nodesx,i,1); + Node *node =&nodesx->ndata[nodex->id]; + index_t index=SEGMENT(node->firstseg); + + do + { + SegmentX *segmentx=LookupSegmentX(segmentsx,index,1); + + if(segmentx->node1==nodex->id) + { + segmentsx->sdata[index].node1=i; + + index++; + + if(index>=segmentsx->number) + break; + + segmentx=LookupSegmentX(segmentsx,index,1); + + if(segmentx->node1!=nodex->id) + break; + } + else + { + segmentsx->sdata[index].node2=i; + + if(segmentsx->sdata[index].next2==NO_NODE) + break; + else + index=segmentsx->sdata[index].next2; + } + } + while(1); + + if(!((i+1)%10000)) + { + printf("\rIndexing Nodes: Nodes=%d",i+1); + fflush(stdout); + } + } + + /* Unmap from memory */ + + if(!option_slim) + { + nodesx->xdata=UnmapFile(nodesx->filename); + segmentsx->xdata=UnmapFile(segmentsx->filename); + } + + /* Print the final message */ + + printf("\rIndexed Nodes: Nodes=%d \n",nodesx->number); + fflush(stdout); +} + + +/*++++++++++++++++++++++++++++++++++++++ + Save the segment list to a file. + + SegmentsX* segmentsx The set of segments to save. + + const char *filename The name of the file to save. + ++++++++++++++++++++++++++++++++++++++*/ + +void SaveSegmentList(SegmentsX* segmentsx,const char *filename) +{ + index_t i; + int fd; + Segments *segments; + int super_number=0,normal_number=0; + + /* Check the start conditions */ + + assert(segmentsx->sdata); /* Must have sdata filled in => real segments */ + + /* Print the start message */ + + printf("Writing Segments: Segments=0"); + fflush(stdout); + + /* Count the number of super-segments and normal segments */ + + for(i=0;inumber;i++) + { + if(IsSuperSegment(&segmentsx->sdata[i])) + super_number++; + if(IsNormalSegment(&segmentsx->sdata[i])) + normal_number++; + } + + /* Fill in a Segments structure with the offset of the real data in the file after + the Segment structure itself. */ + + segments=calloc(1,sizeof(Segments)); + + assert(segments); /* Check calloc() worked */ + + segments->number=segmentsx->number; + segments->snumber=super_number; + segments->nnumber=normal_number; + + segments->data=NULL; + segments->segments=NULL; + + /* Write out the Segments structure and then the real data. */ + + fd=OpenFile(filename); + + WriteFile(fd,segments,sizeof(Segments)); + + for(i=0;inumber;i++) + { + WriteFile(fd,&segmentsx->sdata[i],sizeof(Segment)); + + if(!((i+1)%10000)) + { + printf("\rWriting Segments: Segments=%d",i+1); + fflush(stdout); + } + } + + CloseFile(fd); + + /* Print the final message */ + + printf("\rWrote Segments: Segments=%d \n",segments->number); + fflush(stdout); + + /* Free the fake Segments */ + + free(segments); +} + + +/*++++++++++++++++++++++++++++++++++++++ + Calculate the distance between two nodes. + + distance_t DistanceX Returns the distance between the extended nodes. + + NodeX *nodex1 The starting node. + + NodeX *nodex2 The end node. + ++++++++++++++++++++++++++++++++++++++*/ + +static distance_t DistanceX(NodeX *nodex1,NodeX *nodex2) +{ + double dlon = latlong_to_radians(nodex1->longitude) - latlong_to_radians(nodex2->longitude); + double dlat = latlong_to_radians(nodex1->latitude) - latlong_to_radians(nodex2->latitude); + double lat1 = latlong_to_radians(nodex1->latitude); + double lat2 = latlong_to_radians(nodex2->latitude); + + double a1,a2,a,sa,c,d; + + if(dlon==0 && dlat==0) + return 0; + + a1 = sin (dlat / 2); + a2 = sin (dlon / 2); + a = (a1 * a1) + cos (lat1) * cos (lat2) * a2 * a2; + sa = sqrt (a); + if (sa <= 1.0) + {c = 2 * asin (sa);} + else + {c = 2 * asin (1.0);} + d = 6378.137 * c; + + return km_to_distance(d); +} diff --git a/src/segmentsx.h b/src/segmentsx.h new file mode 100644 index 0000000..ac19a77 --- /dev/null +++ b/src/segmentsx.h @@ -0,0 +1,100 @@ +/*************************************** + $Header: /home/amb/routino/src/RCS/segmentsx.h,v 1.20 2010/03/19 19:47:09 amb Exp $ + + A header file for the extended segments. + + Part of the Routino routing software. + ******************/ /****************** + This file Copyright 2008-2010 Andrew M. Bishop + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + ***************************************/ + + +#ifndef SEGMENTSX_H +#define SEGMENTSX_H /*+ To stop multiple inclusions. +*/ + +#include + +#include "typesx.h" +#include "types.h" + + +/* Data structures */ + + +/*+ An extended structure used for processing. +*/ +struct _SegmentX +{ + node_t node1; /*+ The id of the starting node. +*/ + node_t node2; /*+ The id of the finishing node. +*/ + + way_t way; /*+ The id of the way. +*/ + + distance_t distance; /*+ The distance between the nodes. +*/ +}; + + +/*+ A structure containing a set of segments (memory format). +*/ +struct _SegmentsX +{ + char *filename; /*+ The name of the temporary file. +*/ + int fd; /*+ The file descriptor of the temporary file. +*/ + + uint32_t xnumber; /*+ The number of unsorted extended nodes. +*/ + + SegmentX *xdata; /*+ The extended segment data (unsorted). +*/ + SegmentX cached[2]; /*+ Two cached segments read from the file in slim mode. +*/ + + uint32_t number; /*+ How many entries are still useful? +*/ + + node_t *idata; /*+ The extended segment data (sorted by node1 then node2). +*/ + index_t *firstnode; /*+ The first segment index for each node. +*/ + + Segment *sdata; /*+ The segment data (same order as n1data). +*/ +}; + + +/* Functions */ + + +SegmentsX *NewSegmentList(int append); +void FreeSegmentList(SegmentsX *segmentsx,int keep); + +void SaveSegmentList(SegmentsX *segmentsx,const char *filename); + +SegmentX *LookupSegmentX(SegmentsX* segmentsx,index_t index,int position); + +index_t IndexFirstSegmentX(SegmentsX* segmentsx,node_t node); + +index_t IndexNextSegmentX(SegmentsX* segmentsx,index_t segindex,index_t nodeindex); + +void AppendSegment(SegmentsX* segmentsx,way_t way,node_t node1,node_t node2,distance_t distance); + +void SortSegmentList(SegmentsX* segmentsx); + +void RemoveBadSegments(NodesX *nodesx,SegmentsX *segmentsx); + +void UpdateSegments(SegmentsX *segmentsx,NodesX *nodesx,WaysX *waysx); + +void RotateSegments(SegmentsX* segmentsx); + +void DeduplicateSegments(SegmentsX* segmentsx,NodesX *nodesx,WaysX *waysx); + +void CreateRealSegments(SegmentsX *segmentsx,WaysX *waysx); + +void IndexSegments(SegmentsX* segmentsx,NodesX *nodesx); + + +#endif /* SEGMENTSX_H */ diff --git a/src/sorting.c b/src/sorting.c new file mode 100644 index 0000000..54bfdf0 --- /dev/null +++ b/src/sorting.c @@ -0,0 +1,650 @@ +/*************************************** + $Header: /home/amb/routino/src/RCS/sorting.c,v 1.8 2010/04/09 15:15:02 amb Exp $ + + Merge sort functions. + + Part of the Routino routing software. + ******************/ /****************** + This file Copyright 2009 Andrew M. Bishop + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + ***************************************/ + + +#include +#include +#include +#include + +#include "functions.h" + + +/* Variables */ + +/*+ The command line '--tmpdir' option or its default value. +*/ +extern char *option_tmpdirname; + +/*+ The amount of RAM to use for filesorting. +*/ +extern size_t option_filesort_ramsize; + + +/*++++++++++++++++++++++++++++++++++++++ + A function to sort the contents of a file of fixed length objects using a + limited amount of RAM. + + The data is sorted using a "Merge sort" http://en.wikipedia.org/wiki/Merge_sort + and in particular an "external sort" http://en.wikipedia.org/wiki/External_sorting. + The individual sort steps and the merge step both use a "Heap sort" + http://en.wikipedia.org/wiki/Heapsort. The combination of the two should work well + if the data is already partially sorted. + + int fd_in The file descriptor of the input file (opened for reading and at the beginning). + + int fd_out The file descriptor of the output file (opened for writing and empty). + + size_t itemsize The size of each item in the file that needs sorting. + + int (*compare)(const void*, const void*) The comparison function (identical to qsort if the + data to be sorted is an array of things not pointers). + + int (*buildindex)(void *,index_t) If non-NULL then this function is called for each item, if it + returns 1 then it is written to the output file. + ++++++++++++++++++++++++++++++++++++++*/ + +void filesort_fixed(int fd_in,int fd_out,size_t itemsize,int (*compare)(const void*,const void*),int (*buildindex)(void*,index_t)) +{ + int *fds=NULL,*heap=NULL; + int nfiles=0,ndata=0; + index_t count=0,total=0; + size_t nitems=option_filesort_ramsize/(itemsize+sizeof(void*)); + void *data=NULL,**datap=NULL; + char *filename; + int i,more=1; + + /* Allocate the RAM buffer and other bits */ + + data=malloc(nitems*itemsize); + datap=malloc(nitems*sizeof(void*)); + + filename=(char*)malloc(strlen(option_tmpdirname)+24); + + /* Loop around, fill the buffer, sort the data and write a temporary file */ + + do + { + int fd,n=0; + + /* Read in the data and create pointers */ + + for(i=0;i0 && + compare(datap[heap[index]],datap[heap[(index-1)/2]])<0) + { + int newindex; + int temp; + + newindex=(index-1)/2; + + temp=heap[index]; + heap[index]=heap[newindex]; + heap[newindex]=temp; + + index=newindex; + } + } + + /* Repeatedly pull out the root of the heap and refill from the same file */ + + ndata=nfiles; + + do + { + int index=0; + + if(!buildindex || buildindex(datap[heap[0]],count)) + { + WriteFile(fd_out,datap[heap[0]],itemsize); + count++; + } + + if(ReadFile(fds[heap[0]],datap[heap[0]],itemsize)) + { + ndata--; + heap[0]=heap[ndata]; + } + + /* Bubble down the new value */ + + while((2*index+2)0 || + compare(datap[heap[index]],datap[heap[2*index+2]])>0)) + { + int newindex; + int temp; + + if(compare(datap[heap[2*index+1]],datap[heap[2*index+2]])<0) + newindex=2*index+1; + else + newindex=2*index+2; + + temp=heap[newindex]; + heap[newindex]=heap[index]; + heap[index]=temp; + + index=newindex; + } + + if((2*index+2)==ndata && + compare(datap[heap[index]],datap[heap[2*index+1]])>0) + { + int newindex; + int temp; + + newindex=2*index+1; + + temp=heap[newindex]; + heap[newindex]=heap[index]; + heap[index]=temp; + } + } + while(ndata>0); + + /* Tidy up */ + + tidy_and_exit: + + if(fds) + { + for(i=0;ilargestitemsize) + largestitemsize=itemsize; + + *(FILESORT_VARINT*)(data+ramused)=itemsize; + + ramused+=FILESORT_VARSIZE; + + ReadFile(fd_in,data+ramused,itemsize); + + *--datap=data+ramused; /* points to real data */ + + ramused+=itemsize; + + ramused =FILESORT_VARALIGN*((ramused+FILESORT_VARSIZE-1)/FILESORT_VARALIGN); + ramused+=FILESORT_VARALIGN-FILESORT_VARSIZE; + + total++; + n++; + + if(ReadFile(fd_in,&nextitemsize,FILESORT_VARSIZE)) + { + more=0; + break; + } + } + + if(n==0) + break; + + /* Sort the data pointers using a heap sort */ + + heapsort(datap,n,compare); + + /* Shortcut if all read in and sorted at once */ + + if(nfiles==0 && !more) + { + for(i=0;i0 && + compare(datap[heap[index]],datap[heap[(index-1)/2]])<0) + { + int newindex; + int temp; + + newindex=(index-1)/2; + + temp=heap[index]; + heap[index]=heap[newindex]; + heap[newindex]=temp; + + index=newindex; + } + } + + /* Repeatedly pull out the root of the heap and refill from the same file */ + + ndata=nfiles; + + do + { + int index=0; + FILESORT_VARINT itemsize; + + if(!buildindex || buildindex(datap[heap[0]],count)) + { + itemsize=*(FILESORT_VARINT*)(datap[heap[0]]-FILESORT_VARSIZE); + + WriteFile(fd_out,datap[heap[0]]-FILESORT_VARSIZE,itemsize+FILESORT_VARSIZE); + count++; + } + + if(ReadFile(fds[heap[0]],&itemsize,FILESORT_VARSIZE)) + { + ndata--; + heap[0]=heap[ndata]; + } + else + { + *(FILESORT_VARINT*)(datap[heap[0]]-FILESORT_VARSIZE)=itemsize; + + ReadFile(fds[heap[0]],datap[heap[0]],itemsize); + } + + /* Bubble down the new value */ + + while((2*index+2)0 || + compare(datap[heap[index]],datap[heap[2*index+2]])>0)) + { + int newindex; + int temp; + + if(compare(datap[heap[2*index+1]],datap[heap[2*index+2]])<0) + newindex=2*index+1; + else + newindex=2*index+2; + + temp=heap[newindex]; + heap[newindex]=heap[index]; + heap[index]=temp; + + index=newindex; + } + + if((2*index+2)==ndata && + compare(datap[heap[index]],datap[heap[2*index+1]])>0) + { + int newindex; + int temp; + + newindex=2*index+1; + + temp=heap[newindex]; + heap[newindex]=heap[index]; + heap[index]=temp; + } + } + while(ndata>0); + + /* Tidy up */ + + tidy_and_exit: + + if(fds) + { + for(i=0;i0 && + compare(datap[index],datap[(index-1)/2])>0) /* reversed compared to filesort() above */ + { + int newindex; + void *temp; + + newindex=(index-1)/2; + + temp=datap[index]; + datap[index]=datap[newindex]; + datap[newindex]=temp; + + index=newindex; + } + } + + /* Repeatedly pull out the root of the heap and swap with the bottom item */ + + for(i=nitems-1;i>0;i--) + { + int index=0; + void *temp; + + temp=datap[index]; + datap[index]=datap[i]; + datap[i]=temp; + + /* Bubble down the new value (upside-down, put largest at top) */ + + while((2*index+2)0) /* reversed compared to filesort() above */ + newindex=2*index+1; + else + newindex=2*index+2; + + temp=datap[newindex]; + datap[newindex]=datap[index]; + datap[index]=temp; + + index=newindex; + } + + if((2*index+2)==i && + compare(datap[index],datap[2*index+1])<0) /* reversed compared to filesort() above */ + { + int newindex; + void *temp; + + newindex=2*index+1; + + temp=datap[newindex]; + datap[newindex]=datap[index]; + datap[index]=temp; + } + } +} diff --git a/src/superx.c b/src/superx.c new file mode 100644 index 0000000..45b8227 --- /dev/null +++ b/src/superx.c @@ -0,0 +1,480 @@ +/*************************************** + $Header: /home/amb/routino/src/RCS/superx.c,v 1.38 2010/07/05 19:05:51 amb Exp $ + + Super-Segment data type functions. + + Part of the Routino routing software. + ******************/ /****************** + This file Copyright 2008-2010 Andrew M. Bishop + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + ***************************************/ + + +#include +#include +#include + +#include "results.h" +#include "functions.h" +#include "nodesx.h" +#include "segmentsx.h" +#include "waysx.h" +#include "superx.h" +#include "ways.h" + + +/* Variables */ + +/*+ The command line '--slim' option. +*/ +extern int option_slim; + +/* Local Functions */ + +static Results *FindRoutesWay(NodesX *nodesx,SegmentsX *segmentsx,WaysX *waysx,node_t start,Way *match,int iteration); + + +/*++++++++++++++++++++++++++++++++++++++ + Select the super-segments from the list of segments. + + NodesX *nodesx The nodes. + + SegmentsX *segmentsx The segments. + + WaysX *waysx The ways. + ++++++++++++++++++++++++++++++++++++++*/ + +void ChooseSuperNodes(NodesX *nodesx,SegmentsX *segmentsx,WaysX *waysx) +{ + index_t i; + int nnodes=0; + + /* Check the start conditions */ + + assert(segmentsx->firstnode); /* Must have firstnode filled in => segments are updated */ + + /* Print the start message */ + + printf("Finding Super-Nodes: Nodes=0 Super-Nodes=0"); + fflush(stdout); + + /* Map into memory */ + + if(!option_slim) + { + segmentsx->xdata=MapFile(segmentsx->filename); + waysx->xdata=MapFile(waysx->filename); + } + + /* Find super-nodes */ + + for(i=0;inumber;i++) + { + int difference=0; + index_t index1,index2; + + index1=IndexFirstSegmentX(segmentsx,i); + + while(index1!=NO_SEGMENT) + { + SegmentX *segmentx1=LookupSegmentX(segmentsx,index1,1); + WayX *wayx1=LookupWayX(waysx,segmentx1->way,1); + + index1=IndexNextSegmentX(segmentsx,index1,i); + index2=index1; + + while(index2!=NO_SEGMENT) + { + SegmentX *segmentx2=LookupSegmentX(segmentsx,index2,2); + WayX *wayx2=LookupWayX(waysx,segmentx2->way,2); + + /* If the ways are different in any way and there is a type of traffic that can use both ... */ + + if(WaysCompare(&wayx1->way,&wayx2->way)) + if(wayx1->way.allow & wayx2->way.allow) + { + difference=1; + break; + } + + index2=IndexNextSegmentX(segmentsx,index2,i); + } + + if(difference) + break; + } + + /* Store the node if there is a difference in the connected ways that could affect routing. */ + + if(difference) + { + nodesx->super[i]++; + + nnodes++; + } + + if(!((i+1)%10000)) + { + printf("\rFinding Super-Nodes: Nodes=%d Super-Nodes=%d",i+1,nnodes); + fflush(stdout); + } + } + + /* Unmap from memory */ + + if(!option_slim) + { + segmentsx->xdata=UnmapFile(segmentsx->filename); + waysx->xdata=UnmapFile(waysx->filename); + } + + /* Print the final message */ + + printf("\rFound Super-Nodes: Nodes=%d Super-Nodes=%d \n",nodesx->number,nnodes); + fflush(stdout); +} + + +/*++++++++++++++++++++++++++++++++++++++ + Create the super-segments. + + SegmentsX *CreateSuperSegments Creates the super segments. + + NodesX *nodesx The nodes. + + SegmentsX *segmentsx The segments. + + WaysX *waysx The ways. + + int iteration The current super-node / super-segment iteration number. + ++++++++++++++++++++++++++++++++++++++*/ + +SegmentsX *CreateSuperSegments(NodesX *nodesx,SegmentsX *segmentsx,WaysX *waysx,int iteration) +{ + index_t i; + SegmentsX *supersegmentsx; + int sn=0,ss=0; + + /* Check the start conditions */ + + assert(segmentsx->firstnode); /* Must have firstnode filled in => segments are updated */ + + /* Print the start message */ + + printf("Creating Super-Segments: Super-Nodes=0 Super-Segments=0"); + fflush(stdout); + + /* Map into memory */ + + if(!option_slim) + { + segmentsx->xdata=MapFile(segmentsx->filename); + waysx->xdata=MapFile(waysx->filename); + } + + /* Create super-segments for each super-node. */ + + supersegmentsx=NewSegmentList(0); + + for(i=0;inumber;i++) + { + if(nodesx->super[i]>iteration) + { + index_t index,first; + + index=first=IndexFirstSegmentX(segmentsx,i); + + while(index!=NO_SEGMENT) + { + SegmentX *segmentx=LookupSegmentX(segmentsx,index,1); + WayX *wayx=LookupWayX(waysx,segmentx->way,1); + + /* Check that this type of way hasn't already been routed */ + + if(index!=first) + { + index_t otherindex=first; + + while(otherindex!=NO_SEGMENT && otherindex!=index) + { + SegmentX *othersegmentx=LookupSegmentX(segmentsx,otherindex,2); + WayX *otherwayx=LookupWayX(waysx,othersegmentx->way,2); + + if(!WaysCompare(&otherwayx->way,&wayx->way)) + { + wayx=NULL; + break; + } + + otherindex=IndexNextSegmentX(segmentsx,otherindex,i); + } + } + + /* Route the way and store the super-segments. */ + + if(wayx) + { + Results *results=FindRoutesWay(nodesx,segmentsx,waysx,i,&wayx->way,iteration); + Result *result=FirstResult(results); + + while(result) + { + if(result->node!=i && nodesx->super[result->node]>iteration) + { + if(wayx->way.type&Way_OneWay) + { + AppendSegment(supersegmentsx,segmentx->way,i,result->node,DISTANCE((distance_t)result->score)|ONEWAY_1TO2); + AppendSegment(supersegmentsx,segmentx->way,result->node,i,DISTANCE((distance_t)result->score)|ONEWAY_2TO1); + } + else + AppendSegment(supersegmentsx,segmentx->way,i,result->node,DISTANCE((distance_t)result->score)); + + ss++; + } + + result=NextResult(results,result); + } + + FreeResultsList(results); + } + + index=IndexNextSegmentX(segmentsx,index,i); + } + + sn++; + + if(!(sn%10000)) + { + printf("\rCreating Super-Segments: Super-Nodes=%d Super-Segments=%d",sn,ss); + fflush(stdout); + } + } + } + + /* Unmap from memory */ + + if(!option_slim) + { + segmentsx->xdata=UnmapFile(segmentsx->filename); + waysx->xdata=UnmapFile(waysx->filename); + } + + /* Print the final message */ + + printf("\rCreated Super-Segments: Super-Nodes=%d Super-Segments=%d \n",sn,ss); + fflush(stdout); + + return(supersegmentsx); +} + + +/*++++++++++++++++++++++++++++++++++++++ + Merge the segments and super-segments into a new segment list. + + SegmentsX* MergeSuperSegments Returns a new set of merged segments. + + SegmentsX* segmentsx The set of segments to process. + + SegmentsX* supersegmentsx The set of super-segments to merge. + ++++++++++++++++++++++++++++++++++++++*/ + +SegmentsX *MergeSuperSegments(SegmentsX* segmentsx,SegmentsX* supersegmentsx) +{ + index_t i,j; + int m=0,a=0; + SegmentsX* mergedsegmentsx; + + /* Print the start message */ + + printf("Merging: Segments=0 Super-Segments=0 Merged=0 Added=0"); + fflush(stdout); + + /* Map into memory */ + + if(!option_slim) + { + segmentsx->xdata=MapFile(segmentsx->filename); + supersegmentsx->xdata=MapFile(supersegmentsx->filename); + } + + /* Loop through and create a new list of combined segments */ + + mergedsegmentsx=NewSegmentList(0); + + for(i=0,j=0;inumber;i++) + { + int super=0; + SegmentX *segmentx=LookupSegmentX(segmentsx,i,1); + + while(jnumber) + { + SegmentX *supersegmentx=LookupSegmentX(supersegmentsx,j,1); + + if(segmentx->node1 ==supersegmentx->node1 && + segmentx->node2 ==supersegmentx->node2 && + segmentx->distance==supersegmentx->distance) + { + m++; + j++; + /* mark as super-segment and normal segment */ + super=1; + break; + } + else if((segmentx->node1==supersegmentx->node1 && + segmentx->node2==supersegmentx->node2) || + (segmentx->node1==supersegmentx->node1 && + segmentx->node2>supersegmentx->node2) || + (segmentx->node1>supersegmentx->node1)) + { + /* mark as super-segment */ + AppendSegment(mergedsegmentsx,supersegmentx->way,supersegmentx->node1,supersegmentx->node2,supersegmentx->distance|SEGMENT_SUPER); + a++; + j++; + } + else + { + /* mark as normal segment */ + break; + } + } + + if(super) + AppendSegment(mergedsegmentsx,segmentx->way,segmentx->node1,segmentx->node2,segmentx->distance|SEGMENT_SUPER|SEGMENT_NORMAL); + else + AppendSegment(mergedsegmentsx,segmentx->way,segmentx->node1,segmentx->node2,segmentx->distance|SEGMENT_NORMAL); + + if(!((i+1)%10000)) + { + printf("\rMerging: Segments=%d Super-Segments=%d Merged=%d Added=%d",i+1,j,m,a); + fflush(stdout); + } + } + + /* Unmap from memory */ + + if(!option_slim) + { + segmentsx->xdata=UnmapFile(segmentsx->filename); + supersegmentsx->xdata=UnmapFile(supersegmentsx->filename); + } + + /* Print the final message */ + + printf("\rMerged: Segments=%d Super-Segments=%d Merged=%d Added=%d \n",segmentsx->number,supersegmentsx->number,m,a); + fflush(stdout); + + return(mergedsegmentsx); +} + + +/*++++++++++++++++++++++++++++++++++++++ + Find all routes from a specified node to any node in the specified list that follows a certain type of way. + + Results *FindRoutesWay Returns a set of results. + + NodesX *nodesx The set of nodes to use. + + SegmentsX *segmentsx The set of segments to use. + + WaysX *waysx The set of ways to use. + + node_t start The start node. + + Way *match The way that the route must match. + + int iteration The current super-node / super-segment iteration number. + ++++++++++++++++++++++++++++++++++++++*/ + +static Results *FindRoutesWay(NodesX *nodesx,SegmentsX *segmentsx,WaysX *waysx,node_t start,Way *match,int iteration) +{ + Results *results; + Queue *queue; + index_t node1,node2; + Result *result1,*result2; + index_t index; + WayX *wayx; + + /* Insert the first node into the queue */ + + results=NewResultsList(8); + + queue=NewQueueList(); + + result1=InsertResult(results,start); + + ZeroResult(result1); + + InsertInQueue(queue,result1); + + /* Loop across all nodes in the queue */ + + while((result1=PopFromQueue(queue))) + { + node1=result1->node; + + index=IndexFirstSegmentX(segmentsx,node1); + + while(index!=NO_SEGMENT) + { + SegmentX *segmentx=LookupSegmentX(segmentsx,index,2); /* must use 2 here */ + distance_t cumulative_distance; + + if(segmentx->distance&ONEWAY_2TO1) + goto endloop; + + node2=segmentx->node2; + + if(result1->prev==node2) + goto endloop; + + wayx=LookupWayX(waysx,segmentx->way,2); + + if(WaysCompare(&wayx->way,match)) + goto endloop; + + cumulative_distance=(distance_t)result1->score+DISTANCE(segmentx->distance); + + result2=FindResult(results,node2); + + if(!result2) /* New end node */ + { + result2=InsertResult(results,node2); + result2->prev=node1; + result2->next=NO_NODE; + result2->score=cumulative_distance; + result2->sortby=cumulative_distance; + + if(nodesx->super[node2]<=iteration) + InsertInQueue(queue,result2); + } + else if(cumulative_distancescore) + { + result2->prev=node1; + result2->score=cumulative_distance; + result2->sortby=cumulative_distance; + + if(nodesx->super[node2]<=iteration) + InsertInQueue(queue,result2); + } + + endloop: + + index=IndexNextSegmentX(segmentsx,index,node1); + } + } + + FreeQueueList(queue); + + return(results); +} diff --git a/src/superx.h b/src/superx.h new file mode 100644 index 0000000..cb330df --- /dev/null +++ b/src/superx.h @@ -0,0 +1,38 @@ +/*************************************** + $Header: /home/amb/routino/src/RCS/superx.h,v 1.7 2009/09/06 15:51:09 amb Exp $ + + Header for super-node and super-segment functions. + + Part of the Routino routing software. + ******************/ /****************** + This file Copyright 2008,2009 Andrew M. Bishop + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + ***************************************/ + + +#ifndef SUPERX_H +#define SUPERX_H /*+ To stop multiple inclusions. +*/ + +#include "typesx.h" + + +void ChooseSuperNodes(NodesX *nodesx,SegmentsX *segmentsx,WaysX *waysx); + +SegmentsX *CreateSuperSegments(NodesX *nodesx,SegmentsX *segmentsx,WaysX *waysx,int iteration); + +SegmentsX* MergeSuperSegments(SegmentsX* segmentsx,SegmentsX* supersegmentsx); + + +#endif /* SUPERX_H */ diff --git a/src/tagging.c b/src/tagging.c new file mode 100644 index 0000000..814580f --- /dev/null +++ b/src/tagging.c @@ -0,0 +1,574 @@ +/*************************************** + $Header: /home/amb/routino/src/RCS/tagging.c,v 1.2 2010/05/23 10:18:59 amb Exp $ + + Load the tagging rules from a file and the functions for handling them. + + Part of the Routino routing software. + ******************/ /****************** + This file Copyright 2010 Andrew M. Bishop + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + ***************************************/ + + +#include +#include +#include + +#include "functions.h" +#include "tagging.h" +#include "xmlparse.h" + + +/* Global variables */ + +TaggingRuleList NodeRules={NULL,0}; +TaggingRuleList WayRules={NULL,0}; +TaggingRuleList RelationRules={NULL,0}; + + +/* Local variables */ + +TaggingRuleList *current_list=NULL; +TaggingRule *current_rule=NULL; + + +/* Local functions */ + +static void apply_actions(TaggingRule *rule,int match,TagList *input,TagList *output); + + +/* The XML tag processing function prototypes */ + +//static int xmlDeclaration_function(const char *_tag_,int _type_,const char *version,const char *encoding); +//static int RoutinoTaggingType_function(const char *_tag_,int _type_); +static int WayType_function(const char *_tag_,int _type_); +static int IfType_function(const char *_tag_,int _type_,const char *k,const char *v); +static int RelationType_function(const char *_tag_,int _type_); +static int OutputType_function(const char *_tag_,int _type_,const char *k,const char *v); +static int SetType_function(const char *_tag_,int _type_,const char *k,const char *v); +static int NodeType_function(const char *_tag_,int _type_); + + +/* The XML tag definitions */ + +/*+ The NodeType type tag. +*/ +static xmltag NodeType_tag= + {"node", + 0, {NULL}, + NodeType_function, + {NULL}}; + +/*+ The SetType type tag. +*/ +static xmltag SetType_tag= + {"set", + 2, {"k","v"}, + SetType_function, + {NULL}}; + +/*+ The OutputType type tag. +*/ +static xmltag OutputType_tag= + {"output", + 2, {"k","v"}, + OutputType_function, + {NULL}}; + +/*+ The RelationType type tag. +*/ +static xmltag RelationType_tag= + {"relation", + 0, {NULL}, + RelationType_function, + {NULL}}; + +/*+ The IfType type tag. +*/ +static xmltag IfType_tag= + {"if", + 2, {"k","v"}, + IfType_function, + {&SetType_tag,&OutputType_tag,NULL}}; + +/*+ The WayType type tag. +*/ +static xmltag WayType_tag= + {"way", + 0, {NULL}, + WayType_function, + {&IfType_tag,NULL}}; + +/*+ The RoutinoTaggingType type tag. +*/ +static xmltag RoutinoTaggingType_tag= + {"routino-tagging", + 0, {NULL}, + NULL, + {&NodeType_tag,&WayType_tag,&RelationType_tag,NULL}}; + +/*+ The xmlDeclaration type tag. +*/ +static xmltag xmlDeclaration_tag= + {"xml", + 2, {"version","encoding"}, + NULL, + {NULL}}; + + +/*+ The complete set of tags at the top level. +*/ +static xmltag *xml_toplevel_tags[]={&xmlDeclaration_tag,&RoutinoTaggingType_tag,NULL}; + + +/* The XML tag processing functions */ + + +/*++++++++++++++++++++++++++++++++++++++ + The function that is called when the NodeType XSD type is seen + + int NodeType_function Returns 0 if no error occured or something else otherwise. + + const char *_tag_ Set to the name of the element tag that triggered this function call. + + int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag. + ++++++++++++++++++++++++++++++++++++++*/ + +static int NodeType_function(const char *_tag_,int _type_) +{ + if(_type_&XMLPARSE_TAG_START) + current_list=&NodeRules; + + return(0); +} + + +/*++++++++++++++++++++++++++++++++++++++ + The function that is called when the SetType XSD type is seen + + int SetType_function Returns 0 if no error occured or something else otherwise. + + const char *_tag_ Set to the name of the element tag that triggered this function call. + + int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag. + + const char *k The contents of the 'k' attribute (or NULL if not defined). + + const char *v The contents of the 'v' attribute (or NULL if not defined). + ++++++++++++++++++++++++++++++++++++++*/ + +static int SetType_function(const char *_tag_,int _type_,const char *k,const char *v) +{ + if(_type_&XMLPARSE_TAG_START) + AppendTaggingAction(current_rule,k,v,0); + + return(0); +} + + +/*++++++++++++++++++++++++++++++++++++++ + The function that is called when the OutputType XSD type is seen + + int OutputType_function Returns 0 if no error occured or something else otherwise. + + const char *_tag_ Set to the name of the element tag that triggered this function call. + + int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag. + + const char *k The contents of the 'k' attribute (or NULL if not defined). + + const char *v The contents of the 'v' attribute (or NULL if not defined). + ++++++++++++++++++++++++++++++++++++++*/ + +static int OutputType_function(const char *_tag_,int _type_,const char *k,const char *v) +{ + if(_type_&XMLPARSE_TAG_START) + AppendTaggingAction(current_rule,k,v,1); + + return(0); +} + + +/*++++++++++++++++++++++++++++++++++++++ + The function that is called when the RelationType XSD type is seen + + int RelationType_function Returns 0 if no error occured or something else otherwise. + + const char *_tag_ Set to the name of the element tag that triggered this function call. + + int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag. + ++++++++++++++++++++++++++++++++++++++*/ + +static int RelationType_function(const char *_tag_,int _type_) +{ + if(_type_&XMLPARSE_TAG_START) + current_list=&RelationRules; + + return(0); +} + + +/*++++++++++++++++++++++++++++++++++++++ + The function that is called when the IfType XSD type is seen + + int IfType_function Returns 0 if no error occured or something else otherwise. + + const char *_tag_ Set to the name of the element tag that triggered this function call. + + int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag. + + const char *k The contents of the 'k' attribute (or NULL if not defined). + + const char *v The contents of the 'v' attribute (or NULL if not defined). + ++++++++++++++++++++++++++++++++++++++*/ + +static int IfType_function(const char *_tag_,int _type_,const char *k,const char *v) +{ + if(_type_&XMLPARSE_TAG_START) + { + current_rule=AppendTaggingRule(current_list,k,v); + } + + return(0); +} + + +/*++++++++++++++++++++++++++++++++++++++ + The function that is called when the WayType XSD type is seen + + int WayType_function Returns 0 if no error occured or something else otherwise. + + const char *_tag_ Set to the name of the element tag that triggered this function call. + + int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag. + ++++++++++++++++++++++++++++++++++++++*/ + +static int WayType_function(const char *_tag_,int _type_) +{ + if(_type_&XMLPARSE_TAG_START) + current_list=&WayRules; + + return(0); +} + + +/*++++++++++++++++++++++++++++++++++++++ + The function that is called when the RoutinoTaggingType XSD type is seen + + int RoutinoTaggingType_function Returns 0 if no error occured or something else otherwise. + + const char *_tag_ Set to the name of the element tag that triggered this function call. + + int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag. + ++++++++++++++++++++++++++++++++++++++*/ + +//static int RoutinoTaggingType_function(const char *_tag_,int _type_) +//{ +// return(0); +//} + + +/*++++++++++++++++++++++++++++++++++++++ + The function that is called when the XML declaration is seen + + int xmlDeclaration_function Returns 0 if no error occured or something else otherwise. + + const char *_tag_ Set to the name of the element tag that triggered this function call. + + int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag. + + const char *version The contents of the 'version' attribute (or NULL if not defined). + + const char *encoding The contents of the 'encoding' attribute (or NULL if not defined). + ++++++++++++++++++++++++++++++++++++++*/ + +//static int xmlDeclaration_function(const char *_tag_,int _type_,const char *version,const char *encoding) +//{ +// return(0); +//} + + +/*++++++++++++++++++++++++++++++++++++++ + The XML tagging rules parser. + + int ParseXMLTaggingRules Returns 0 if OK or something else in case of an error. + + const char *filename The name of the file to read. + ++++++++++++++++++++++++++++++++++++++*/ + +int ParseXMLTaggingRules(const char *filename) +{ + int retval; + + if(!ExistsFile(filename)) + { + fprintf(stderr,"Error: Specified tagging rules file '%s' does not exist.\n",filename); + return(1); + } + + FILE *file=fopen(filename,"r"); + + if(!file) + { + fprintf(stderr,"Error: Cannot open tagging rules file '%s' for reading.\n",filename); + return(1); + } + + retval=ParseXML(file,xml_toplevel_tags,XMLPARSE_UNKNOWN_ATTR_ERRNONAME); + + fclose(file); + + if(retval) + return(1); + + return(0); +} + + +/*++++++++++++++++++++++++++++++++++++++ + Append a tagging rule to the list of rules. + + TaggingRule *AppendTaggingRule Returns the latest rule (the just added one). + + TaggingRuleList *rules The list of rules to add to. + + const char *k The tag key. + + const char *v The tag value. + ++++++++++++++++++++++++++++++++++++++*/ + +TaggingRule *AppendTaggingRule(TaggingRuleList *rules,const char *k,const char *v) +{ + if((rules->nrules%16)==0) + rules->rules=(TaggingRule*)realloc((void*)rules->rules,(rules->nrules+16)*sizeof(TaggingRule)); + + rules->nrules++; + + if(k) + rules->rules[rules->nrules-1].k=strcpy(malloc(strlen(k)+1),k); + else + rules->rules[rules->nrules-1].k=NULL; + + if(v) + rules->rules[rules->nrules-1].v=strcpy(malloc(strlen(v)+1),v); + else + rules->rules[rules->nrules-1].v=NULL; + + rules->rules[rules->nrules-1].nactions=0; + rules->rules[rules->nrules-1].actions=NULL; + + return(&rules->rules[rules->nrules-1]); +} + + +/*++++++++++++++++++++++++++++++++++++++ + Append a tagging action to a tagging rule. + + TaggingRule *rule The rule to add the action to. + + const char *k The tag key. + + const char *v The tag value. + + int output Set to 1 if this is an output rule. + ++++++++++++++++++++++++++++++++++++++*/ + +void AppendTaggingAction(TaggingRule *rule,const char *k,const char *v,int output) +{ + if((rule->nactions%16)==0) + rule->actions=(TaggingAction*)realloc((void*)rule->actions,(rule->nactions+16)*sizeof(TaggingAction)); + + rule->nactions++; + + rule->actions[rule->nactions-1].output=output; + + if(k) + rule->actions[rule->nactions-1].k=strcpy(malloc(strlen(k)+1),k); + else + rule->actions[rule->nactions-1].k=NULL; + + if(v) + rule->actions[rule->nactions-1].v=strcpy(malloc(strlen(v)+1),v); + else + rule->actions[rule->nactions-1].v=NULL; +} + + +/*++++++++++++++++++++++++++++++++++++++ + Create a new TagList structure. + + TagList *NewTagList Returns the new allocated TagList. + ++++++++++++++++++++++++++++++++++++++*/ + +TagList *NewTagList(void) +{ + return((TagList*)calloc(sizeof(TagList),1)); +} + + +/*++++++++++++++++++++++++++++++++++++++ + Append a tag to the list of tags. + + TagList *tags The list of tags to add to. + + const char *k The tag key. + + const char *v The tag value. + ++++++++++++++++++++++++++++++++++++++*/ + +void AppendTag(TagList *tags,const char *k,const char *v) +{ + if((tags->ntags%16)==0) + { + int i; + + tags->k=(char**)realloc((void*)tags->k,(tags->ntags+16)*sizeof(char*)); + tags->v=(char**)realloc((void*)tags->v,(tags->ntags+16)*sizeof(char*)); + + for(i=tags->ntags;i<(tags->ntags+16);i++) + tags->k[i]=tags->v[i]=NULL; + } + + tags->k[tags->ntags]=strcpy(realloc(tags->k[tags->ntags],strlen(k)+1),k); + tags->v[tags->ntags]=strcpy(realloc(tags->v[tags->ntags],strlen(v)+1),v); + + tags->ntags++; +} + + +/*++++++++++++++++++++++++++++++++++++++ + Modify an existing tag or append a new tag to the list of tags. + + TagList *tags The list of tags to modify. + + const char *k The tag key. + + const char *v The tag value. + ++++++++++++++++++++++++++++++++++++++*/ + +void ModifyTag(TagList *tags,const char *k,const char *v) +{ + int i; + + for(i=0;intags;i++) + if(!strcmp(tags->k[i],k)) + { + tags->v[i]=strcpy(realloc(tags->v[i],strlen(v)+1),v); + return; + } + + AppendTag(tags,k,v); +} + + +/*++++++++++++++++++++++++++++++++++++++ + Delete a tag list and the contents. + + TagList *tags The list of tags to delete. + ++++++++++++++++++++++++++++++++++++++*/ + +void DeleteTagList(TagList *tags) +{ + int i; + + for(i=0;intags;i++) + { + if(tags->k[i]) free(tags->k[i]); + if(tags->v[i]) free(tags->v[i]); + } + + if(tags->ntags) + { + free(tags->k); + free(tags->v); + } + + free(tags); +} + + +/*++++++++++++++++++++++++++++++++++++++ + Apply a set of tagging rules to a set of tags. + + TagList *ApplyTaggingRules Returns the list of output tags after modification. + + TaggingRuleList *rules The tagging rules to apply. + + TagList *tags The tags to be modified. + ++++++++++++++++++++++++++++++++++++++*/ + +TagList *ApplyTaggingRules(TaggingRuleList *rules,TagList *tags) +{ + TagList *result=NewTagList(); + int i,j; + + for(i=0;inrules;i++) + { + if(rules->rules[i].k && rules->rules[i].v) + { + for(j=0;jntags;j++) + if(!strcmp(tags->k[j],rules->rules[i].k) && !strcmp(tags->v[j],rules->rules[i].v)) + apply_actions(&rules->rules[i],j,tags,result); + } + else if(rules->rules[i].k && !rules->rules[i].v) + { + for(j=0;jntags;j++) + if(!strcmp(tags->k[j],rules->rules[i].k)) + apply_actions(&rules->rules[i],j,tags,result); + } + else if(!rules->rules[i].k && rules->rules[i].v) + { + for(j=0;jntags;j++) + if(!strcmp(tags->v[j],rules->rules[i].v)) + apply_actions(&rules->rules[i],j,tags,result); + } + else /* if(!rules->rules[i].k && !rules->rules[i].v) */ + { + for(j=0;jntags;j++) + apply_actions(&rules->rules[i],j,tags,result); + } + } + + return(result); +} + + +/*++++++++++++++++++++++++++++++++++++++ + Apply a set of actions to a matching tag. + + TaggingRule *rule The rule that matched (containing the actions). + + int match The matching tag number. + + TagList *input The input tags. + + TagList *output The output tags. + ++++++++++++++++++++++++++++++++++++++*/ + +static void apply_actions(TaggingRule *rule,int match,TagList *input,TagList *output) +{ + int i; + + for(i=0;inactions;i++) + { + char *k,*v; + + if(rule->actions[i].k) + k=rule->actions[i].k; + else + k=input->k[match]; + + if(rule->actions[i].v) + v=rule->actions[i].v; + else + v=input->v[match]; + + if(rule->actions[i].output) + ModifyTag(output,k,v); + else + ModifyTag(input,k,v); + } +} diff --git a/src/tagging.h b/src/tagging.h new file mode 100644 index 0000000..4dfdbb8 --- /dev/null +++ b/src/tagging.h @@ -0,0 +1,95 @@ +/*************************************** + $Header: /home/amb/routino/src/RCS/tagging.h,v 1.2 2010/05/23 10:18:59 amb Exp $ + + The data types for the tagging rules. + + Part of the Routino routing software. + ******************/ /****************** + This file Copyright 2010 Andrew M. Bishop + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + ***************************************/ + +#ifndef TAGGING_H +#define TAGGING_H /*+ To stop multiple inclusions. +*/ + + +/* Data types */ + +/*+ A structure to contain the tagging action. +*/ +typedef struct _TaggingAction +{ + int output; /*+ A flag to indicate if the output tags or input tags are to be changed. +*/ + + char *k; /*+ The tag key (or NULL). +*/ + char *v; /*+ The tag value (or NULL). +*/ +} + TaggingAction; + + +/*+ A structure to contain the tagging rule. +*/ +typedef struct _TaggingRule +{ + char *k; /*+ The tag key (or NULL). +*/ + char *v; /*+ The tag value (or NULL). +*/ + + TaggingAction *actions; /*+ The actions to take. +*/ + int nactions; /*+ The number of actions. +*/ +} + TaggingRule; + + +/*+ A structure to contain the list of rules and associated information. +*/ +typedef struct _TaggingRuleList +{ + TaggingRule *rules; /*+ The array of rules. +*/ + int nrules; /*+ The number of rules. +*/ +} + TaggingRuleList; + + +/*+ A structure to hold a list of tags to be processed. +*/ +typedef struct _TagList +{ + int ntags; /*+ The number of tags. +*/ + + char **k; /*+ The list of tag keys. +*/ + char **v; /*+ The list of tag values. +*/ +} + TagList; + + +/* Variables */ + +extern TaggingRuleList NodeRules; +extern TaggingRuleList WayRules; +extern TaggingRuleList RelationRules; + + +/* Functions */ + +int ParseXMLTaggingRules(const char *filename); + +TaggingRule *AppendTaggingRule(TaggingRuleList *rules,const char *k,const char *v); +void AppendTaggingAction(TaggingRule *rule,const char *k,const char *v,int output); + +TagList *NewTagList(void); +void AppendTag(TagList *tags,const char *k,const char *v); +void ModifyTag(TagList *tags,const char *k,const char *v); +void DeleteTagList(TagList *tags); + +TagList *ApplyTaggingRules(TaggingRuleList *rules,TagList *tags); + + +#endif /* TAGGING_H */ diff --git a/src/tagmodifier.c b/src/tagmodifier.c new file mode 100644 index 0000000..06bcc87 --- /dev/null +++ b/src/tagmodifier.c @@ -0,0 +1,626 @@ +/*************************************** + $Header: /home/amb/routino/src/RCS/tagmodifier.c,v 1.5 2010/05/30 18:18:54 amb Exp $ + + Test application for OSM XML file parser / tagging rule testing. + + Part of the Routino routing software. + ******************/ /****************** + This file Copyright 2010 Andrew M. Bishop + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + ***************************************/ + + +#include +#include +#include +#include +#include + +#include "functions.h" +#include "xmlparse.h" +#include "tagging.h" + + +/* Local variables */ + +static long nnodes=0,nways=0,nrelations=0; +TagList *current_tags=NULL; + + +/* Local functions */ + +static void print_usage(int detail); + + +/* The XML tag processing function prototypes */ + +static int xmlDeclaration_function(const char *_tag_,int _type_,const char *version,const char *encoding); +static int osmType_function(const char *_tag_,int _type_,const char *version,const char *generator); +static int relationType_function(const char *_tag_,int _type_,const char *id,const char *timestamp,const char *uid,const char *user,const char *visible,const char *version,const char *action); +static int wayType_function(const char *_tag_,int _type_,const char *id,const char *timestamp,const char *uid,const char *user,const char *visible,const char *version,const char *action); +static int memberType_function(const char *_tag_,int _type_,const char *type,const char *ref,const char *role); +static int ndType_function(const char *_tag_,int _type_,const char *ref); +static int nodeType_function(const char *_tag_,int _type_,const char *id,const char *lat,const char *lon,const char *timestamp,const char *uid,const char *user,const char *visible,const char *version,const char *action); +static int tagType_function(const char *_tag_,int _type_,const char *k,const char *v); +static int boundType_function(const char *_tag_,int _type_,const char *box,const char *origin); +static int boundsType_function(const char *_tag_,int _type_,const char *minlat,const char *minlon,const char *maxlat,const char *maxlon,const char *origin); + + +/* The XML tag definitions */ + +/*+ The boundsType type tag. +*/ +static xmltag boundsType_tag= + {"bounds", + 5, {"minlat","minlon","maxlat","maxlon","origin"}, + boundsType_function, + {NULL}}; + +/*+ The boundType type tag. +*/ +static xmltag boundType_tag= + {"bound", + 2, {"box","origin"}, + boundType_function, + {NULL}}; + +/*+ The tagType type tag. +*/ +static xmltag tagType_tag= + {"tag", + 2, {"k","v"}, + tagType_function, + {NULL}}; + +/*+ The nodeType type tag. +*/ +static xmltag nodeType_tag= + {"node", + 9, {"id","lat","lon","timestamp","uid","user","visible","version","action"}, + nodeType_function, + {&tagType_tag,NULL}}; + +/*+ The ndType type tag. +*/ +static xmltag ndType_tag= + {"nd", + 1, {"ref"}, + ndType_function, + {NULL}}; + +/*+ The memberType type tag. +*/ +static xmltag memberType_tag= + {"member", + 3, {"type","ref","role"}, + memberType_function, + {NULL}}; + +/*+ The wayType type tag. +*/ +static xmltag wayType_tag= + {"way", + 7, {"id","timestamp","uid","user","visible","version","action"}, + wayType_function, + {&ndType_tag,&tagType_tag,NULL}}; + +/*+ The relationType type tag. +*/ +static xmltag relationType_tag= + {"relation", + 7, {"id","timestamp","uid","user","visible","version","action"}, + relationType_function, + {&memberType_tag,&tagType_tag,NULL}}; + +/*+ The osmType type tag. +*/ +static xmltag osmType_tag= + {"osm", + 2, {"version","generator"}, + osmType_function, + {&boundsType_tag,&boundType_tag,&nodeType_tag,&wayType_tag,&relationType_tag,NULL}}; + +/*+ The xmlDeclaration type tag. +*/ +static xmltag xmlDeclaration_tag= + {"xml", + 2, {"version","encoding"}, + xmlDeclaration_function, + {NULL}}; + + +/*+ The complete set of tags at the top level. +*/ +static xmltag *xml_toplevel_tags[]={&xmlDeclaration_tag,&osmType_tag,NULL}; + + +/* The XML tag processing functions */ + + +/*++++++++++++++++++++++++++++++++++++++ + The function that is called when the boundsType XSD type is seen + + int boundsType_function Returns 0 if no error occured or something else otherwise. + + const char *_tag_ Set to the name of the element tag that triggered this function call. + + int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag. + + const char *minlat The contents of the 'minlat' attribute (or NULL if not defined). + + const char *minlon The contents of the 'minlon' attribute (or NULL if not defined). + + const char *maxlat The contents of the 'maxlat' attribute (or NULL if not defined). + + const char *maxlon The contents of the 'maxlon' attribute (or NULL if not defined). + + const char *origin The contents of the 'origin' attribute (or NULL if not defined). + ++++++++++++++++++++++++++++++++++++++*/ + +static int boundsType_function(const char *_tag_,int _type_,const char *minlat,const char *minlon,const char *maxlat,const char *maxlon,const char *origin) +{ + printf(" <%s%s",(_type_==XMLPARSE_TAG_END)?"/":"",_tag_); + if(minlat) printf(" minlat=\"%s\"",ParseXML_Encode_Safe_XML(minlat)); + if(minlon) printf(" minlon=\"%s\"",ParseXML_Encode_Safe_XML(minlon)); + if(maxlat) printf(" maxlat=\"%s\"",ParseXML_Encode_Safe_XML(maxlat)); + if(maxlon) printf(" maxlon=\"%s\"",ParseXML_Encode_Safe_XML(maxlon)); + if(origin) printf(" origin=\"%s\"",ParseXML_Encode_Safe_XML(origin)); + printf("%s>\n",(_type_==(XMLPARSE_TAG_START|XMLPARSE_TAG_END))?" /":""); + return(0); +} + + +/*++++++++++++++++++++++++++++++++++++++ + The function that is called when the boundType XSD type is seen + + int boundType_function Returns 0 if no error occured or something else otherwise. + + const char *_tag_ Set to the name of the element tag that triggered this function call. + + int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag. + + const char *box The contents of the 'box' attribute (or NULL if not defined). + + const char *origin The contents of the 'origin' attribute (or NULL if not defined). + ++++++++++++++++++++++++++++++++++++++*/ + +static int boundType_function(const char *_tag_,int _type_,const char *box,const char *origin) +{ + printf(" <%s%s",(_type_==XMLPARSE_TAG_END)?"/":"",_tag_); + if(box) printf(" box=\"%s\"",ParseXML_Encode_Safe_XML(box)); + if(origin) printf(" origin=\"%s\"",ParseXML_Encode_Safe_XML(origin)); + printf("%s>\n",(_type_==(XMLPARSE_TAG_START|XMLPARSE_TAG_END))?" /":""); + return(0); +} + + +/*++++++++++++++++++++++++++++++++++++++ + The function that is called when the tagType XSD type is seen + + int tagType_function Returns 0 if no error occured or something else otherwise. + + const char *_tag_ Set to the name of the element tag that triggered this function call. + + int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag. + + const char *k The contents of the 'k' attribute (or NULL if not defined). + + const char *v The contents of the 'v' attribute (or NULL if not defined). + ++++++++++++++++++++++++++++++++++++++*/ + +static int tagType_function(const char *_tag_,int _type_,const char *k,const char *v) +{ + if(_type_&XMLPARSE_TAG_START) + { + XMLPARSE_ASSERT_STRING(_tag_,k); + XMLPARSE_ASSERT_STRING(_tag_,v); + + AppendTag(current_tags,k,v); + } + + return(0); +} + + +/*++++++++++++++++++++++++++++++++++++++ + The function that is called when the nodeType XSD type is seen + + int nodeType_function Returns 0 if no error occured or something else otherwise. + + const char *_tag_ Set to the name of the element tag that triggered this function call. + + int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag. + + const char *id The contents of the 'id' attribute (or NULL if not defined). + + const char *lat The contents of the 'lat' attribute (or NULL if not defined). + + const char *lon The contents of the 'lon' attribute (or NULL if not defined). + + const char *timestamp The contents of the 'timestamp' attribute (or NULL if not defined). + + const char *uid The contents of the 'uid' attribute (or NULL if not defined). + + const char *user The contents of the 'user' attribute (or NULL if not defined). + + const char *visible The contents of the 'visible' attribute (or NULL if not defined). + + const char *version The contents of the 'version' attribute (or NULL if not defined). + + const char *action The contents of the 'action' attribute (or NULL if not defined). + ++++++++++++++++++++++++++++++++++++++*/ + +static int nodeType_function(const char *_tag_,int _type_,const char *id,const char *lat,const char *lon,const char *timestamp,const char *uid,const char *user,const char *visible,const char *version,const char *action) +{ + if(_type_&XMLPARSE_TAG_START) + { + nnodes++; + + if(!(nnodes%1000)) + fprintf(stderr,"\rReading: Lines=%ld Nodes=%ld Ways=%ld Relations=%ld",ParseXML_LineNumber(),nnodes,nways,nrelations); + + current_tags=NewTagList(); + } + + if(_type_&XMLPARSE_TAG_END) + { + TagList *result=ApplyTaggingRules(&NodeRules,current_tags); + int i; + + for(i=0;intags;i++) + { + printf(" k[i])); + printf(" v=\"%s\"",ParseXML_Encode_Safe_XML(result->v[i])); + printf(" />\n"); + } + + DeleteTagList(current_tags); + DeleteTagList(result); + } + + printf(" <%s%s",(_type_==XMLPARSE_TAG_END)?"/":"",_tag_); + if(id) printf(" id=\"%s\"",ParseXML_Encode_Safe_XML(id)); + if(lat) printf(" lat=\"%s\"",ParseXML_Encode_Safe_XML(lat)); + if(lon) printf(" lon=\"%s\"",ParseXML_Encode_Safe_XML(lon)); + if(timestamp) printf(" timestamp=\"%s\"",ParseXML_Encode_Safe_XML(timestamp)); + if(uid) printf(" uid=\"%s\"",ParseXML_Encode_Safe_XML(uid)); + if(user) printf(" user=\"%s\"",ParseXML_Encode_Safe_XML(user)); + if(visible) printf(" visible=\"%s\"",ParseXML_Encode_Safe_XML(visible)); + if(version) printf(" version=\"%s\"",ParseXML_Encode_Safe_XML(version)); + if(action) printf(" action=\"%s\"",ParseXML_Encode_Safe_XML(action)); + printf("%s>\n",(_type_==(XMLPARSE_TAG_START|XMLPARSE_TAG_END))?" /":""); + return(0); +} + + +/*++++++++++++++++++++++++++++++++++++++ + The function that is called when the ndType XSD type is seen + + int ndType_function Returns 0 if no error occured or something else otherwise. + + const char *_tag_ Set to the name of the element tag that triggered this function call. + + int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag. + + const char *ref The contents of the 'ref' attribute (or NULL if not defined). + ++++++++++++++++++++++++++++++++++++++*/ + +static int ndType_function(const char *_tag_,int _type_,const char *ref) +{ + printf(" <%s%s",(_type_==XMLPARSE_TAG_END)?"/":"",_tag_); + if(ref) printf(" ref=\"%s\"",ParseXML_Encode_Safe_XML(ref)); + printf("%s>\n",(_type_==(XMLPARSE_TAG_START|XMLPARSE_TAG_END))?" /":""); + return(0); +} + + +/*++++++++++++++++++++++++++++++++++++++ + The function that is called when the memberType XSD type is seen + + int memberType_function Returns 0 if no error occured or something else otherwise. + + const char *_tag_ Set to the name of the element tag that triggered this function call. + + int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag. + + const char *type The contents of the 'type' attribute (or NULL if not defined). + + const char *ref The contents of the 'ref' attribute (or NULL if not defined). + + const char *role The contents of the 'role' attribute (or NULL if not defined). + ++++++++++++++++++++++++++++++++++++++*/ + +static int memberType_function(const char *_tag_,int _type_,const char *type,const char *ref,const char *role) +{ + printf(" <%s%s",(_type_==XMLPARSE_TAG_END)?"/":"",_tag_); + if(type) printf(" type=\"%s\"",ParseXML_Encode_Safe_XML(type)); + if(ref) printf(" ref=\"%s\"",ParseXML_Encode_Safe_XML(ref)); + if(role) printf(" role=\"%s\"",ParseXML_Encode_Safe_XML(role)); + printf("%s>\n",(_type_==(XMLPARSE_TAG_START|XMLPARSE_TAG_END))?" /":""); + return(0); +} + + +/*++++++++++++++++++++++++++++++++++++++ + The function that is called when the wayType XSD type is seen + + int wayType_function Returns 0 if no error occured or something else otherwise. + + const char *_tag_ Set to the name of the element tag that triggered this function call. + + int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag. + + const char *id The contents of the 'id' attribute (or NULL if not defined). + + const char *timestamp The contents of the 'timestamp' attribute (or NULL if not defined). + + const char *uid The contents of the 'uid' attribute (or NULL if not defined). + + const char *user The contents of the 'user' attribute (or NULL if not defined). + + const char *visible The contents of the 'visible' attribute (or NULL if not defined). + + const char *version The contents of the 'version' attribute (or NULL if not defined). + + const char *action The contents of the 'action' attribute (or NULL if not defined). + ++++++++++++++++++++++++++++++++++++++*/ + +static int wayType_function(const char *_tag_,int _type_,const char *id,const char *timestamp,const char *uid,const char *user,const char *visible,const char *version,const char *action) +{ + if(_type_&XMLPARSE_TAG_START) + { + nways++; + + if(!(nways%1000)) + fprintf(stderr,"\rReading: Lines=%ld Nodes=%ld Ways=%ld Relations=%ld",ParseXML_LineNumber(),nnodes,nways,nrelations); + + current_tags=NewTagList(); + } + + if(_type_&XMLPARSE_TAG_END) + { + TagList *result=ApplyTaggingRules(&WayRules,current_tags); + int i; + + for(i=0;intags;i++) + { + printf(" k[i])); + printf(" v=\"%s\"",ParseXML_Encode_Safe_XML(result->v[i])); + printf(" />\n"); + } + + DeleteTagList(current_tags); + DeleteTagList(result); + } + + printf(" <%s%s",(_type_==XMLPARSE_TAG_END)?"/":"",_tag_); + if(id) printf(" id=\"%s\"",ParseXML_Encode_Safe_XML(id)); + if(timestamp) printf(" timestamp=\"%s\"",ParseXML_Encode_Safe_XML(timestamp)); + if(uid) printf(" uid=\"%s\"",ParseXML_Encode_Safe_XML(uid)); + if(user) printf(" user=\"%s\"",ParseXML_Encode_Safe_XML(user)); + if(visible) printf(" visible=\"%s\"",ParseXML_Encode_Safe_XML(visible)); + if(version) printf(" version=\"%s\"",ParseXML_Encode_Safe_XML(version)); + if(action) printf(" action=\"%s\"",ParseXML_Encode_Safe_XML(action)); + printf("%s>\n",(_type_==(XMLPARSE_TAG_START|XMLPARSE_TAG_END))?" /":""); + return(0); +} + + +/*++++++++++++++++++++++++++++++++++++++ + The function that is called when the relationType XSD type is seen + + int relationType_function Returns 0 if no error occured or something else otherwise. + + const char *_tag_ Set to the name of the element tag that triggered this function call. + + int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag. + + const char *id The contents of the 'id' attribute (or NULL if not defined). + + const char *timestamp The contents of the 'timestamp' attribute (or NULL if not defined). + + const char *uid The contents of the 'uid' attribute (or NULL if not defined). + + const char *user The contents of the 'user' attribute (or NULL if not defined). + + const char *visible The contents of the 'visible' attribute (or NULL if not defined). + + const char *version The contents of the 'version' attribute (or NULL if not defined). + + const char *action The contents of the 'action' attribute (or NULL if not defined). + ++++++++++++++++++++++++++++++++++++++*/ + +static int relationType_function(const char *_tag_,int _type_,const char *id,const char *timestamp,const char *uid,const char *user,const char *visible,const char *version,const char *action) +{ + if(_type_&XMLPARSE_TAG_START) + { + nrelations++; + + if(!(nrelations%1000)) + fprintf(stderr,"\rReading: Lines=%ld Nodes=%ld Ways=%ld Relations=%ld",ParseXML_LineNumber(),nnodes,nways,nrelations); + + current_tags=NewTagList(); + } + + if(_type_&XMLPARSE_TAG_END) + { + TagList *result=ApplyTaggingRules(&RelationRules,current_tags); + int i; + + for(i=0;intags;i++) + { + printf(" k[i])); + printf(" v=\"%s\"",ParseXML_Encode_Safe_XML(result->v[i])); + printf(" />\n"); + } + + DeleteTagList(current_tags); + DeleteTagList(result); + } + + printf(" <%s%s",(_type_==XMLPARSE_TAG_END)?"/":"",_tag_); + if(id) printf(" id=\"%s\"",ParseXML_Encode_Safe_XML(id)); + if(timestamp) printf(" timestamp=\"%s\"",ParseXML_Encode_Safe_XML(timestamp)); + if(uid) printf(" uid=\"%s\"",ParseXML_Encode_Safe_XML(uid)); + if(user) printf(" user=\"%s\"",ParseXML_Encode_Safe_XML(user)); + if(visible) printf(" visible=\"%s\"",ParseXML_Encode_Safe_XML(visible)); + if(version) printf(" version=\"%s\"",ParseXML_Encode_Safe_XML(version)); + if(action) printf(" action=\"%s\"",ParseXML_Encode_Safe_XML(action)); + printf("%s>\n",(_type_==(XMLPARSE_TAG_START|XMLPARSE_TAG_END))?" /":""); + return(0); +} + + +/*++++++++++++++++++++++++++++++++++++++ + The function that is called when the osmType XSD type is seen + + int osmType_function Returns 0 if no error occured or something else otherwise. + + const char *_tag_ Set to the name of the element tag that triggered this function call. + + int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag. + + const char *version The contents of the 'version' attribute (or NULL if not defined). + + const char *generator The contents of the 'generator' attribute (or NULL if not defined). + ++++++++++++++++++++++++++++++++++++++*/ + +static int osmType_function(const char *_tag_,int _type_,const char *version,const char *generator) +{ + printf("<%s%s",(_type_==XMLPARSE_TAG_END)?"/":"",_tag_); + if(version) printf(" version=\"%s\"",ParseXML_Encode_Safe_XML(version)); + if(generator) printf(" generator=\"%s\"",ParseXML_Encode_Safe_XML(generator)); + printf("%s>\n",(_type_==(XMLPARSE_TAG_START|XMLPARSE_TAG_END))?" /":""); + return(0); +} + + +/*++++++++++++++++++++++++++++++++++++++ + The function that is called when the XML declaration is seen + + int xmlDeclaration_function Returns 0 if no error occured or something else otherwise. + + const char *_tag_ Set to the name of the element tag that triggered this function call. + + int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag. + + const char *version The contents of the 'version' attribute (or NULL if not defined). + + const char *encoding The contents of the 'encoding' attribute (or NULL if not defined). + ++++++++++++++++++++++++++++++++++++++*/ + +static int xmlDeclaration_function(const char *_tag_,int _type_,const char *version,const char *encoding) +{ + printf("\n"); + return(0); +} + + +/*++++++++++++++++++++++++++++++++++++++ + The main program for the planetsplitter. + ++++++++++++++++++++++++++++++++++++++*/ + +int main(int argc,char **argv) +{ + char *tagging=NULL,*filename=NULL; + FILE *file; + int arg,retval; + + /* Parse the command line arguments */ + + for(arg=1;arg]\n" + " []\n"); + + if(detail) + fprintf(stderr, + "\n" + "--help Prints this information.\n" + "\n" + "--tagging= The name of the XML file containing the tagging rules\n" + " (defaults to 'tagging.xml' in current directory).\n" + "\n" + " The name of the file to process (by default data is\n" + " read from standard input).\n"); + + exit(!detail); +} diff --git a/src/translations.c b/src/translations.c new file mode 100644 index 0000000..c00fdff --- /dev/null +++ b/src/translations.c @@ -0,0 +1,1050 @@ +/*************************************** + $Header: /home/amb/routino/src/RCS/translations.c,v 1.10 2010/07/03 10:58:37 amb Exp $ + + Load the translations from a file and the functions for handling them. + + Part of the Routino routing software. + ******************/ /****************** + This file Copyright 2010 Andrew M. Bishop + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + ***************************************/ + + +#include +#include +#include + +#include "functions.h" +#include "translations.h" +#include "xmlparse.h" + + +/* Global variables - default English values */ + +char *translate_copyright_creator[2]={"Creator","Routino - http://www.routino.org/"}; +char *translate_copyright_source[2] ={NULL,NULL}; +char *translate_copyright_license[2]={NULL,NULL}; + +char *translate_heading[9]={"South","South-West","West","North-West","North","North-East","East","South-East","South"}; +char *translate_turn[9] ={"Very sharp left","Sharp left","Left","Slight left","Straight on","Slight right","Right","Sharp right","Very sharp right"}; + +char *translate_highway[Way_Count]={"","motorway","trunk road","primary road","secondary road","tertiary road","unclassified road","residential road","service road","track","cycleway","path","steps"}; + +char *translate_route_shortest="Shortest"; +char *translate_route_quickest="Quickest"; + +char *translate_html_waypoint="Waypoint"; +char *translate_html_junction="Junction"; + +char *translate_html_title="%s Route"; +char *translate_html_start[2]={"Start","At %s, head %s"}; +char *translate_html_segment[2]={"Follow","%s for %.3f km, %.1f min"}; +char *translate_html_node[2]={"At","%s, go %s heading %s"}; +char *translate_html_stop[2]={"Stop","At %s"}; +char *translate_html_total[2]={"Total","%.1f km, %.0f minutes"}; + +char *translate_gpx_desc ="%s between 'start' and 'finish' waypoints"; +char *translate_gpx_name ="%s Route"; +char *translate_gpx_step ="%s on '%s' for %.3f km, %.1 min"; +char *translate_gpx_final="Total Journey %.1f km, %d minutes"; + +char *translate_gpx_start="START"; +char *translate_gpx_inter="INTER"; +char *translate_gpx_trip="TRIP"; +char *translate_gpx_finish="FINISH"; + + +/* Local variables */ + +/*+ The language that is to be stored. +*/ +static const char *store_lang=NULL; + +/*+ This current language is to be stored. +*/ +static int store=0; + +/*+ The chosen language has been stored. +*/ +static int stored=0; + + +/* The XML tag processing function prototypes */ + +//static int xmlDeclaration_function(const char *_tag_,int _type_,const char *version,const char *encoding); +//static int RoutinoTranslationsType_function(const char *_tag_,int _type_); +static int languageType_function(const char *_tag_,int _type_,const char *lang); +//static int GPXType_function(const char *_tag_,int _type_); +static int GPXFinalType_function(const char *_tag_,int _type_,const char *text); +static int GPXStepType_function(const char *_tag_,int _type_,const char *text); +static int GPXNameType_function(const char *_tag_,int _type_,const char *text); +static int GPXDescType_function(const char *_tag_,int _type_,const char *text); +//static int HTMLType_function(const char *_tag_,int _type_); +static int HTMLTotalType_function(const char *_tag_,int _type_,const char *string,const char *text); +static int HTMLStopType_function(const char *_tag_,int _type_,const char *string,const char *text); +static int HTMLSegmentType_function(const char *_tag_,int _type_,const char *string,const char *text); +static int HTMLNodeType_function(const char *_tag_,int _type_,const char *string,const char *text); +static int HTMLStartType_function(const char *_tag_,int _type_,const char *string,const char *text); +static int HTMLTitleType_function(const char *_tag_,int _type_,const char *text); +//static int CopyrightType_function(const char *_tag_,int _type_); +static int GPXWaypointType_function(const char *_tag_,int _type_,const char *type,const char *string); +static int HTMLWaypointType_function(const char *_tag_,int _type_,const char *type,const char *string); +static int RouteType_function(const char *_tag_,int _type_,const char *type,const char *string); +static int HighwayType_function(const char *_tag_,int _type_,const char *type,const char *string); +static int HeadingType_function(const char *_tag_,int _type_,const char *direction,const char *string); +static int TurnType_function(const char *_tag_,int _type_,const char *direction,const char *string); +static int CopyrightLicenseType_function(const char *_tag_,int _type_,const char *string,const char *text); +static int CopyrightSourceType_function(const char *_tag_,int _type_,const char *string,const char *text); +static int CopyrightCreatorType_function(const char *_tag_,int _type_,const char *string,const char *text); + + +/* The XML tag definitions */ + +/*+ The CopyrightCreatorType type tag. +*/ +static xmltag CopyrightCreatorType_tag= + {"creator", + 2, {"string","text"}, + CopyrightCreatorType_function, + {NULL}}; + +/*+ The CopyrightSourceType type tag. +*/ +static xmltag CopyrightSourceType_tag= + {"source", + 2, {"string","text"}, + CopyrightSourceType_function, + {NULL}}; + +/*+ The CopyrightLicenseType type tag. +*/ +static xmltag CopyrightLicenseType_tag= + {"license", + 2, {"string","text"}, + CopyrightLicenseType_function, + {NULL}}; + +/*+ The TurnType type tag. +*/ +static xmltag TurnType_tag= + {"turn", + 2, {"direction","string"}, + TurnType_function, + {NULL}}; + +/*+ The HeadingType type tag. +*/ +static xmltag HeadingType_tag= + {"heading", + 2, {"direction","string"}, + HeadingType_function, + {NULL}}; + +/*+ The HighwayType type tag. +*/ +static xmltag HighwayType_tag= + {"highway", + 2, {"type","string"}, + HighwayType_function, + {NULL}}; + +/*+ The RouteType type tag. +*/ +static xmltag RouteType_tag= + {"route", + 2, {"type","string"}, + RouteType_function, + {NULL}}; + +/*+ The HTMLWaypointType type tag. +*/ +static xmltag HTMLWaypointType_tag= + {"waypoint", + 2, {"type","string"}, + HTMLWaypointType_function, + {NULL}}; + +/*+ The GPXWaypointType type tag. +*/ +static xmltag GPXWaypointType_tag= + {"waypoint", + 2, {"type","string"}, + GPXWaypointType_function, + {NULL}}; + +/*+ The CopyrightType type tag. +*/ +static xmltag CopyrightType_tag= + {"copyright", + 0, {NULL}, + NULL, + {&CopyrightCreatorType_tag,&CopyrightSourceType_tag,&CopyrightLicenseType_tag,NULL}}; + +/*+ The HTMLTitleType type tag. +*/ +static xmltag HTMLTitleType_tag= + {"title", + 1, {"text"}, + HTMLTitleType_function, + {NULL}}; + +/*+ The HTMLStartType type tag. +*/ +static xmltag HTMLStartType_tag= + {"start", + 2, {"string","text"}, + HTMLStartType_function, + {NULL}}; + +/*+ The HTMLNodeType type tag. +*/ +static xmltag HTMLNodeType_tag= + {"node", + 2, {"string","text"}, + HTMLNodeType_function, + {NULL}}; + +/*+ The HTMLSegmentType type tag. +*/ +static xmltag HTMLSegmentType_tag= + {"segment", + 2, {"string","text"}, + HTMLSegmentType_function, + {NULL}}; + +/*+ The HTMLStopType type tag. +*/ +static xmltag HTMLStopType_tag= + {"stop", + 2, {"string","text"}, + HTMLStopType_function, + {NULL}}; + +/*+ The HTMLTotalType type tag. +*/ +static xmltag HTMLTotalType_tag= + {"total", + 2, {"string","text"}, + HTMLTotalType_function, + {NULL}}; + +/*+ The HTMLType type tag. +*/ +static xmltag HTMLType_tag= + {"output-html", + 0, {NULL}, + NULL, + {&HTMLWaypointType_tag,&HTMLTitleType_tag,&HTMLStartType_tag,&HTMLNodeType_tag,&HTMLSegmentType_tag,&HTMLStopType_tag,&HTMLTotalType_tag,NULL}}; + +/*+ The GPXDescType type tag. +*/ +static xmltag GPXDescType_tag= + {"desc", + 1, {"text"}, + GPXDescType_function, + {NULL}}; + +/*+ The GPXNameType type tag. +*/ +static xmltag GPXNameType_tag= + {"name", + 1, {"text"}, + GPXNameType_function, + {NULL}}; + +/*+ The GPXStepType type tag. +*/ +static xmltag GPXStepType_tag= + {"step", + 1, {"text"}, + GPXStepType_function, + {NULL}}; + +/*+ The GPXFinalType type tag. +*/ +static xmltag GPXFinalType_tag= + {"final", + 1, {"text"}, + GPXFinalType_function, + {NULL}}; + +/*+ The GPXType type tag. +*/ +static xmltag GPXType_tag= + {"output-gpx", + 0, {NULL}, + NULL, + {&GPXWaypointType_tag,&GPXDescType_tag,&GPXNameType_tag,&GPXStepType_tag,&GPXFinalType_tag,NULL}}; + +/*+ The languageType type tag. +*/ +static xmltag languageType_tag= + {"language", + 1, {"lang"}, + languageType_function, + {&CopyrightType_tag,&TurnType_tag,&HeadingType_tag,&HighwayType_tag,&RouteType_tag,&HTMLType_tag,&GPXType_tag,NULL}}; + +/*+ The RoutinoTranslationsType type tag. +*/ +static xmltag RoutinoTranslationsType_tag= + {"routino-translations", + 0, {NULL}, + NULL, + {&languageType_tag,NULL}}; + +/*+ The xmlDeclaration type tag. +*/ +static xmltag xmlDeclaration_tag= + {"xml", + 2, {"version","encoding"}, + NULL, + {NULL}}; + + +/*+ The complete set of tags at the top level. +*/ +static xmltag *xml_toplevel_tags[]={&xmlDeclaration_tag,&RoutinoTranslationsType_tag,NULL}; + + +/* The XML tag processing functions */ + + +/*++++++++++++++++++++++++++++++++++++++ + The function that is called when the CopyrightCreatorType XSD type is seen + + int CopyrightCreatorType_function Returns 0 if no error occured or something else otherwise. + + const char *_tag_ Set to the name of the element tag that triggered this function call. + + int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag. + + const char *string The contents of the 'string' attribute (or NULL if not defined). + + const char *text The contents of the 'text' attribute (or NULL if not defined). + ++++++++++++++++++++++++++++++++++++++*/ + +static int CopyrightCreatorType_function(const char *_tag_,int _type_,const char *string,const char *text) +{ + if(_type_&XMLPARSE_TAG_START && store) + { + XMLPARSE_ASSERT_STRING(_tag_,string); + XMLPARSE_ASSERT_STRING(_tag_,text); + + translate_copyright_creator[0]=strcpy(malloc(strlen(string)+1),string); + translate_copyright_creator[1]=strcpy(malloc(strlen(text)+1),text); + } + + return(0); +} + + +/*++++++++++++++++++++++++++++++++++++++ + The function that is called when the CopyrightSourceType XSD type is seen + + int CopyrightSourceType_function Returns 0 if no error occured or something else otherwise. + + const char *_tag_ Set to the name of the element tag that triggered this function call. + + int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag. + + const char *string The contents of the 'string' attribute (or NULL if not defined). + + const char *text The contents of the 'text' attribute (or NULL if not defined). + ++++++++++++++++++++++++++++++++++++++*/ + +static int CopyrightSourceType_function(const char *_tag_,int _type_,const char *string,const char *text) +{ + if(_type_&XMLPARSE_TAG_START && store) + { + XMLPARSE_ASSERT_STRING(_tag_,string); + XMLPARSE_ASSERT_STRING(_tag_,text); + + translate_copyright_source[0]=strcpy(malloc(strlen(string)+1),string); + translate_copyright_source[1]=strcpy(malloc(strlen(text)+1),text); + } + + return(0); +} + + +/*++++++++++++++++++++++++++++++++++++++ + The function that is called when the CopyrightLicenseType XSD type is seen + + int CopyrightLicenseType_function Returns 0 if no error occured or something else otherwise. + + const char *_tag_ Set to the name of the element tag that triggered this function call. + + int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag. + + const char *string The contents of the 'string' attribute (or NULL if not defined). + + const char *text The contents of the 'text' attribute (or NULL if not defined). + ++++++++++++++++++++++++++++++++++++++*/ + +static int CopyrightLicenseType_function(const char *_tag_,int _type_,const char *string,const char *text) +{ + if(_type_&XMLPARSE_TAG_START && store) + { + XMLPARSE_ASSERT_STRING(_tag_,string); + XMLPARSE_ASSERT_STRING(_tag_,text); + + translate_copyright_license[0]=strcpy(malloc(strlen(string)+1),string); + translate_copyright_license[1]=strcpy(malloc(strlen(text)+1),text); + } + + return(0); +} + + +/*++++++++++++++++++++++++++++++++++++++ + The function that is called when the TurnType XSD type is seen + + int TurnType_function Returns 0 if no error occured or something else otherwise. + + const char *_tag_ Set to the name of the element tag that triggered this function call. + + int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag. + + const char *direction The contents of the 'direction' attribute (or NULL if not defined). + + const char *string The contents of the 'string' attribute (or NULL if not defined). + ++++++++++++++++++++++++++++++++++++++*/ + +static int TurnType_function(const char *_tag_,int _type_,const char *direction,const char *string) +{ + if(_type_&XMLPARSE_TAG_START && store) + { + int d; + + XMLPARSE_ASSERT_INTEGER(_tag_,direction,d); + XMLPARSE_ASSERT_STRING(_tag_,string); + + d+=4; + + if(d<0 || d>8) + XMLPARSE_INVALID(_tag_,direction); + + translate_turn[d]=strcpy(malloc(strlen(string)+1),string); + } + + return(0); +} + + +/*++++++++++++++++++++++++++++++++++++++ + The function that is called when the HeadingType XSD type is seen + + int HeadingType_function Returns 0 if no error occured or something else otherwise. + + const char *_tag_ Set to the name of the element tag that triggered this function call. + + int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag. + + const char *direction The contents of the 'direction' attribute (or NULL if not defined). + + const char *string The contents of the 'string' attribute (or NULL if not defined). + ++++++++++++++++++++++++++++++++++++++*/ + +static int HeadingType_function(const char *_tag_,int _type_,const char *direction,const char *string) +{ + if(_type_&XMLPARSE_TAG_START && store) + { + int d; + + XMLPARSE_ASSERT_INTEGER(_tag_,direction,d); + XMLPARSE_ASSERT_STRING(_tag_,string); + + d+=4; + + if(d<0 || d>8) + XMLPARSE_INVALID(_tag_,direction); + + translate_heading[d]=strcpy(malloc(strlen(string)+1),string); + } + + return(0); +} + + +/*++++++++++++++++++++++++++++++++++++++ + The function that is called when the HighwayType XSD type is seen + + int HighwayType_function Returns 0 if no error occured or something else otherwise. + + const char *_tag_ Set to the name of the element tag that triggered this function call. + + int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag. + + const char *type The contents of the 'type' attribute (or NULL if not defined). + + const char *string The contents of the 'string' attribute (or NULL if not defined). + ++++++++++++++++++++++++++++++++++++++*/ + +static int HighwayType_function(const char *_tag_,int _type_,const char *type,const char *string) +{ + if(_type_&XMLPARSE_TAG_START && store) + { + Highway highway; + + XMLPARSE_ASSERT_STRING(_tag_,type); + XMLPARSE_ASSERT_STRING(_tag_,string); + + highway=HighwayType(type); + + if(highway==Way_Count) + XMLPARSE_INVALID(_tag_,type); + + translate_highway[highway]=strcpy(malloc(strlen(string)+1),string); + } + + return(0); +} + + +/*++++++++++++++++++++++++++++++++++++++ + The function that is called when the RouteType XSD type is seen + + int RouteType_function Returns 0 if no error occured or something else otherwise. + + const char *_tag_ Set to the name of the element tag that triggered this function call. + + int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag. + + const char *type The contents of the 'type' attribute (or NULL if not defined). + + const char *string The contents of the 'string' attribute (or NULL if not defined). + ++++++++++++++++++++++++++++++++++++++*/ + +static int RouteType_function(const char *_tag_,int _type_,const char *type,const char *string) +{ + if(_type_&XMLPARSE_TAG_START && store) + { + XMLPARSE_ASSERT_STRING(_tag_,type); + XMLPARSE_ASSERT_STRING(_tag_,string); + + if(!strcmp(type,"shortest")) + translate_route_shortest=strcpy(malloc(strlen(string)+1),string); + else if(!strcmp(type,"quickest")) + translate_route_quickest=strcpy(malloc(strlen(string)+1),string); + else + XMLPARSE_INVALID(_tag_,type); + } + + return(0); +} + + +/*++++++++++++++++++++++++++++++++++++++ + The function that is called when the HTMLWaypointType XSD type is seen + + int HTMLWaypointType_function Returns 0 if no error occured or something else otherwise. + + const char *_tag_ Set to the name of the element tag that triggered this function call. + + int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag. + + const char *type The contents of the 'type' attribute (or NULL if not defined). + + const char *string The contents of the 'string' attribute (or NULL if not defined). + ++++++++++++++++++++++++++++++++++++++*/ + +static int HTMLWaypointType_function(const char *_tag_,int _type_,const char *type,const char *string) +{ + if(_type_&XMLPARSE_TAG_START && store) + { + XMLPARSE_ASSERT_STRING(_tag_,type); + XMLPARSE_ASSERT_STRING(_tag_,string); + + if(!strcmp(type,"waypoint")) + { + translate_html_waypoint=malloc(strlen(string)+1+sizeof("")+sizeof("")); + sprintf(translate_html_waypoint,"%s",string); + } + else if(!strcmp(type,"junction")) + translate_html_junction=strcpy(malloc(strlen(string)+1),string); + else + XMLPARSE_INVALID(_tag_,type); + } + + return(0); +} + + +/*++++++++++++++++++++++++++++++++++++++ + The function that is called when the GPXWaypointType XSD type is seen + + int GPXWaypointType_function Returns 0 if no error occured or something else otherwise. + + const char *_tag_ Set to the name of the element tag that triggered this function call. + + int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag. + + const char *type The contents of the 'type' attribute (or NULL if not defined). + + const char *string The contents of the 'string' attribute (or NULL if not defined). + ++++++++++++++++++++++++++++++++++++++*/ + +static int GPXWaypointType_function(const char *_tag_,int _type_,const char *type,const char *string) +{ + if(_type_&XMLPARSE_TAG_START && store) + { + XMLPARSE_ASSERT_STRING(_tag_,type); + XMLPARSE_ASSERT_STRING(_tag_,string); + + if(!strcmp(type,"start")) + translate_gpx_start=strcpy(malloc(strlen(string)+1),string); + else if(!strcmp(type,"inter")) + translate_gpx_inter=strcpy(malloc(strlen(string)+1),string); + else if(!strcmp(type,"trip")) + translate_gpx_trip=strcpy(malloc(strlen(string)+1),string); + else if(!strcmp(type,"finish")) + translate_gpx_finish=strcpy(malloc(strlen(string)+1),string); + else + XMLPARSE_INVALID(_tag_,type); + } + + return(0); +} + + +/*++++++++++++++++++++++++++++++++++++++ + The function that is called when the CopyrightType XSD type is seen + + int CopyrightType_function Returns 0 if no error occured or something else otherwise. + + const char *_tag_ Set to the name of the element tag that triggered this function call. + + int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag. + ++++++++++++++++++++++++++++++++++++++*/ + +//static int CopyrightType_function(const char *_tag_,int _type_) +//{ +// return(0); +//} + + +/*++++++++++++++++++++++++++++++++++++++ + The function that is called when the HTMLTitleType XSD type is seen + + int HTMLTitleType_function Returns 0 if no error occured or something else otherwise. + + const char *_tag_ Set to the name of the element tag that triggered this function call. + + int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag. + + const char *text The contents of the 'text' attribute (or NULL if not defined). + ++++++++++++++++++++++++++++++++++++++*/ + +static int HTMLTitleType_function(const char *_tag_,int _type_,const char *text) +{ + if(_type_&XMLPARSE_TAG_START && store) + { + XMLPARSE_ASSERT_STRING(_tag_,text); + + translate_html_title=strcpy(malloc(strlen(text)+1),text); + } + + return(0); +} + + +/*++++++++++++++++++++++++++++++++++++++ + The function that is called when the HTMLStartType XSD type is seen + + int HTMLStartType_function Returns 0 if no error occured or something else otherwise. + + const char *_tag_ Set to the name of the element tag that triggered this function call. + + int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag. + + const char *string The contents of the 'string' attribute (or NULL if not defined). + + const char *text The contents of the 'text' attribute (or NULL if not defined). + ++++++++++++++++++++++++++++++++++++++*/ + +static int HTMLStartType_function(const char *_tag_,int _type_,const char *string,const char *text) +{ + if(_type_&XMLPARSE_TAG_START && store) + { + XMLPARSE_ASSERT_STRING(_tag_,string); + XMLPARSE_ASSERT_STRING(_tag_,text); + + translate_html_start[0]=strcpy(malloc(strlen(string)+1),string); + translate_html_start[1]=malloc(strlen(text)+1+sizeof("")+sizeof("")); + sprintf(translate_html_start[1],text,"%s","%s"); + } + + return(0); +} + + +/*++++++++++++++++++++++++++++++++++++++ + The function that is called when the HTMLNodeType XSD type is seen + + int HTMLNodeType_function Returns 0 if no error occured or something else otherwise. + + const char *_tag_ Set to the name of the element tag that triggered this function call. + + int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag. + + const char *string The contents of the 'string' attribute (or NULL if not defined). + + const char *text The contents of the 'text' attribute (or NULL if not defined). + ++++++++++++++++++++++++++++++++++++++*/ + +static int HTMLNodeType_function(const char *_tag_,int _type_,const char *string,const char *text) +{ + if(_type_&XMLPARSE_TAG_START && store) + { + XMLPARSE_ASSERT_STRING(_tag_,string); + XMLPARSE_ASSERT_STRING(_tag_,text); + + translate_html_node[0]=strcpy(malloc(strlen(string)+1),string); + translate_html_node[1]=malloc(strlen(text)+1+2*sizeof("")+2*sizeof("")); + sprintf(translate_html_node[1],text,"%s","%s","%s"); + } + + return(0); +} + + +/*++++++++++++++++++++++++++++++++++++++ + The function that is called when the HTMLSegmentType XSD type is seen + + int HTMLSegmentType_function Returns 0 if no error occured or something else otherwise. + + const char *_tag_ Set to the name of the element tag that triggered this function call. + + int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag. + + const char *string The contents of the 'string' attribute (or NULL if not defined). + + const char *text The contents of the 'text' attribute (or NULL if not defined). + ++++++++++++++++++++++++++++++++++++++*/ + +static int HTMLSegmentType_function(const char *_tag_,int _type_,const char *string,const char *text) +{ + if(_type_&XMLPARSE_TAG_START && store) + { + const char *p; + char *q; + + XMLPARSE_ASSERT_STRING(_tag_,string); + XMLPARSE_ASSERT_STRING(_tag_,text); + + translate_html_segment[0]=strcpy(malloc(strlen(string)+1),string); + translate_html_segment[1]=malloc(strlen(text)+1+2*sizeof("")+2*sizeof("")); + + p=text; + q=translate_html_segment[1]; + + while(*p!='%' && *(p+1)!='s') + *q++=*p++; + + p+=2; + strcpy(q,"%s"); q+=sizeof("%s")-1; + + while(*p!='%') + *q++=*p++; + + strcpy(q,""); q+=sizeof("")-1; + + strcpy(q,p); + strcat(q,""); + } + + return(0); +} + + +/*++++++++++++++++++++++++++++++++++++++ + The function that is called when the HTMLStopType XSD type is seen + + int HTMLStopType_function Returns 0 if no error occured or something else otherwise. + + const char *_tag_ Set to the name of the element tag that triggered this function call. + + int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag. + + const char *string The contents of the 'string' attribute (or NULL if not defined). + + const char *text The contents of the 'text' attribute (or NULL if not defined). + ++++++++++++++++++++++++++++++++++++++*/ + +static int HTMLStopType_function(const char *_tag_,int _type_,const char *string,const char *text) +{ + if(_type_&XMLPARSE_TAG_START && store) + { + XMLPARSE_ASSERT_STRING(_tag_,string); + XMLPARSE_ASSERT_STRING(_tag_,text); + + translate_html_stop[0]=strcpy(malloc(strlen(string)+1),string); + translate_html_stop[1]=strcpy(malloc(strlen(text)+1),text); + } + + return(0); +} + + +/*++++++++++++++++++++++++++++++++++++++ + The function that is called when the HTMLTotalType XSD type is seen + + int HTMLTotalType_function Returns 0 if no error occured or something else otherwise. + + const char *_tag_ Set to the name of the element tag that triggered this function call. + + int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag. + + const char *string The contents of the 'string' attribute (or NULL if not defined). + + const char *text The contents of the 'text' attribute (or NULL if not defined). + ++++++++++++++++++++++++++++++++++++++*/ + +static int HTMLTotalType_function(const char *_tag_,int _type_,const char *string,const char *text) +{ + if(_type_&XMLPARSE_TAG_START && store) + { + XMLPARSE_ASSERT_STRING(_tag_,string); + XMLPARSE_ASSERT_STRING(_tag_,text); + + translate_html_total[0]=strcpy(malloc(strlen(string)+1),string); + translate_html_total[1]=strcpy(malloc(strlen(text)+1),text); + } + + return(0); +} + + +/*++++++++++++++++++++++++++++++++++++++ + The function that is called when the HTMLType XSD type is seen + + int HTMLType_function Returns 0 if no error occured or something else otherwise. + + const char *_tag_ Set to the name of the element tag that triggered this function call. + + int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag. + ++++++++++++++++++++++++++++++++++++++*/ + +//static int HTMLType_function(const char *_tag_,int _type_) +//{ +// return(0); +//} + + +/*++++++++++++++++++++++++++++++++++++++ + The function that is called when the GPXDescType XSD type is seen + + int GPXDescType_function Returns 0 if no error occured or something else otherwise. + + const char *_tag_ Set to the name of the element tag that triggered this function call. + + int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag. + + const char *text The contents of the 'text' attribute (or NULL if not defined). + ++++++++++++++++++++++++++++++++++++++*/ + +static int GPXDescType_function(const char *_tag_,int _type_,const char *text) +{ + if(_type_&XMLPARSE_TAG_START && store) + { + XMLPARSE_ASSERT_STRING(_tag_,text); + + translate_gpx_desc=strcpy(malloc(strlen(text)+1),text); + } + + return(0); +} + + +/*++++++++++++++++++++++++++++++++++++++ + The function that is called when the GPXNameType XSD type is seen + + int GPXNameType_function Returns 0 if no error occured or something else otherwise. + + const char *_tag_ Set to the name of the element tag that triggered this function call. + + int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag. + + const char *text The contents of the 'text' attribute (or NULL if not defined). + ++++++++++++++++++++++++++++++++++++++*/ + +static int GPXNameType_function(const char *_tag_,int _type_,const char *text) +{ + if(_type_&XMLPARSE_TAG_START && store) + { + XMLPARSE_ASSERT_STRING(_tag_,text); + + translate_gpx_name=strcpy(malloc(strlen(text)+1),text); + } + + return(0); +} + + +/*++++++++++++++++++++++++++++++++++++++ + The function that is called when the GPXStepType XSD type is seen + + int GPXStepType_function Returns 0 if no error occured or something else otherwise. + + const char *_tag_ Set to the name of the element tag that triggered this function call. + + int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag. + + const char *text The contents of the 'text' attribute (or NULL if not defined). + ++++++++++++++++++++++++++++++++++++++*/ + +static int GPXStepType_function(const char *_tag_,int _type_,const char *text) +{ + if(_type_&XMLPARSE_TAG_START && store) + { + XMLPARSE_ASSERT_STRING(_tag_,text); + + translate_gpx_step=strcpy(malloc(strlen(text)+1),text); + } + + return(0); +} + + +/*++++++++++++++++++++++++++++++++++++++ + The function that is called when the GPXFinalType XSD type is seen + + int GPXFinalType_function Returns 0 if no error occured or something else otherwise. + + const char *_tag_ Set to the name of the element tag that triggered this function call. + + int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag. + + const char *text The contents of the 'text' attribute (or NULL if not defined). + ++++++++++++++++++++++++++++++++++++++*/ + +static int GPXFinalType_function(const char *_tag_,int _type_,const char *text) +{ + if(_type_&XMLPARSE_TAG_START && store) + { + XMLPARSE_ASSERT_STRING(_tag_,text); + + translate_gpx_final=strcpy(malloc(strlen(text)+1),text); + } + + return(0); +} + + +/*++++++++++++++++++++++++++++++++++++++ + The function that is called when the GPXType XSD type is seen + + int GPXType_function Returns 0 if no error occured or something else otherwise. + + const char *_tag_ Set to the name of the element tag that triggered this function call. + + int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag. + ++++++++++++++++++++++++++++++++++++++*/ + +//static int GPXType_function(const char *_tag_,int _type_) +//{ +// return(0); +//} + + +/*++++++++++++++++++++++++++++++++++++++ + The function that is called when the languageType XSD type is seen + + int languageType_function Returns 0 if no error occured or something else otherwise. + + const char *_tag_ Set to the name of the element tag that triggered this function call. + + int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag. + + const char *lang The contents of the 'lang' attribute (or NULL if not defined). + ++++++++++++++++++++++++++++++++++++++*/ + +static int languageType_function(const char *_tag_,int _type_,const char *lang) +{ + static int first=1; + + if(_type_&XMLPARSE_TAG_START) + { + XMLPARSE_ASSERT_STRING(_tag_,lang); + + if(!store_lang && first) + store=1; + else if(store_lang && !strcmp(store_lang,lang)) + store=1; + else + store=0; + + first=0; + } + + if(_type_&XMLPARSE_TAG_END && store) + { + store=0; + stored=1; + } + + return(0); +} + + +/*++++++++++++++++++++++++++++++++++++++ + The function that is called when the RoutinoTranslationsType XSD type is seen + + int RoutinoTranslationsType_function Returns 0 if no error occured or something else otherwise. + + const char *_tag_ Set to the name of the element tag that triggered this function call. + + int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag. + ++++++++++++++++++++++++++++++++++++++*/ + +//static int RoutinoTranslationsType_function(const char *_tag_,int _type_) +//{ +// return(0); +//} + + +/*++++++++++++++++++++++++++++++++++++++ + The function that is called when the XML declaration is seen + + int xmlDeclaration_function Returns 0 if no error occured or something else otherwise. + + const char *_tag_ Set to the name of the element tag that triggered this function call. + + int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag. + + const char *version The contents of the 'version' attribute (or NULL if not defined). + + const char *encoding The contents of the 'encoding' attribute (or NULL if not defined). + ++++++++++++++++++++++++++++++++++++++*/ + +//static int xmlDeclaration_function(const char *_tag_,int _type_,const char *version,const char *encoding) +//{ +// return(0); +//} + + +/*++++++++++++++++++++++++++++++++++++++ + The XML translation parser. + + int ParseXMLTranslations Returns 0 if OK or something else in case of an error. + + const char *filename The name of the file to read. + + const char *language The language to search for (NULL means first in file). + ++++++++++++++++++++++++++++++++++++++*/ + +int ParseXMLTranslations(const char *filename,const char *language) +{ + int retval; + + store_lang=language; + + if(!ExistsFile(filename)) + { + fprintf(stderr,"Error: Specified translations file '%s' does not exist.\n",filename); + return(1); + } + + FILE *file=fopen(filename,"r"); + + if(!file) + { + fprintf(stderr,"Error: Cannot open translations file '%s' for reading.\n",filename); + return(1); + } + + retval=ParseXML(file,xml_toplevel_tags,XMLPARSE_UNKNOWN_ATTR_ERRNONAME|XMLPARSE_RETURN_ATTR_ENCODED); + + fclose(file); + + if(retval) + return(1); + + if(language && !stored) + fprintf(stderr,"Warning: Cannot find translations for language '%s' using English instead.\n",language); + + return(0); +} diff --git a/src/translations.h b/src/translations.h new file mode 100644 index 0000000..13902ed --- /dev/null +++ b/src/translations.h @@ -0,0 +1,69 @@ +/*************************************** + $Header: /home/amb/routino/src/RCS/translations.h,v 1.4 2010/05/29 13:54:23 amb Exp $ + + Load the translations from a file and the functions for handling them. + + Part of the Routino routing software. + ******************/ /****************** + This file Copyright 2010 Andrew M. Bishop + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + ***************************************/ + + +#ifndef TRANSLATIONS_H +#define TRANSLATIONS_H /*+ To stop multiple inclusions. +*/ + +#include "types.h" + + +/* Variable declations */ + +extern char *translate_copyright_creator[2]; +extern char *translate_copyright_source[2]; +extern char *translate_copyright_license[2]; + +extern char *translate_heading[9]; +extern char *translate_turn[9]; + +extern char *translate_highway[Way_Count]; + +extern char *translate_route_shortest; +extern char *translate_route_quickest; + +extern char *translate_html_waypoint; +extern char *translate_html_junction; + +extern char *translate_html_title; +extern char *translate_html_start[2]; +extern char *translate_html_segment[2]; +extern char *translate_html_node[2]; +extern char *translate_html_stop[2]; +extern char *translate_html_total[2]; + +extern char *translate_gpx_desc; +extern char *translate_gpx_name; +extern char *translate_gpx_step; +extern char *translate_gpx_final; + +extern char *translate_gpx_start; +extern char *translate_gpx_inter; +extern char *translate_gpx_trip; +extern char *translate_gpx_finish; + +/* Functions */ + +int ParseXMLTranslations(const char *filename,const char *language); + +#endif /* TRANSLATIONS_H */ diff --git a/src/types.c b/src/types.c new file mode 100644 index 0000000..3670992 --- /dev/null +++ b/src/types.c @@ -0,0 +1,488 @@ +/*************************************** + $Header: /home/amb/routino/src/RCS/types.c,v 1.3 2010/05/27 17:25:23 amb Exp $ + + Functions for handling the data types. + + Part of the Routino routing software. + ******************/ /****************** + This file Copyright 2008-2010 Andrew M. Bishop + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + ***************************************/ + + +#include + +#include "types.h" + + +/*++++++++++++++++++++++++++++++++++++++ + Decide on the type of a way given the "highway" parameter. + + Highway HighwayType Returns the highway type of the way. + + const char *highway The string containing the type of the way. + ++++++++++++++++++++++++++++++++++++++*/ + +Highway HighwayType(const char *highway) +{ + switch(*highway) + { + case 'c': + if(!strcmp(highway,"cycleway")) return(Way_Cycleway); + return(Way_Count); + + case 'm': + if(!strcmp(highway,"motorway")) return(Way_Motorway); + return(Way_Count); + + case 'p': + if(!strcmp(highway,"primary")) return(Way_Primary); + if(!strcmp(highway,"path")) return(Way_Path); + return(Way_Count); + + case 'r': + if(!strcmp(highway,"residential")) return(Way_Residential); + return(Way_Count); + + case 's': + if(!strcmp(highway,"secondary")) return(Way_Secondary); + if(!strcmp(highway,"service")) return(Way_Service); + if(!strcmp(highway,"steps")) return(Way_Steps); + return(Way_Count); + + case 't': + if(!strcmp(highway,"trunk")) return(Way_Trunk); + if(!strcmp(highway,"tertiary")) return(Way_Tertiary); + if(!strcmp(highway,"track")) return(Way_Track); + return(Way_Count); + + case 'u': + if(!strcmp(highway,"unclassified")) return(Way_Unclassified); + return(Way_Count); + + default: + ; + } + + return(Way_Count); +} + + +/*++++++++++++++++++++++++++++++++++++++ + Decide on the type of transport given the name of it. + + Transport TransportType Returns the type of the transport. + + const char *transport The string containing the method of transport. + ++++++++++++++++++++++++++++++++++++++*/ + +Transport TransportType(const char *transport) +{ + switch(*transport) + { + case 'b': + if(!strcmp(transport,"bicycle")) + return(Transport_Bicycle); + break; + + case 'f': + if(!strcmp(transport,"foot")) + return(Transport_Foot); + break; + + case 'g': + if(!strcmp(transport,"goods")) + return(Transport_Goods); + break; + + case 'h': + if(!strcmp(transport,"horse")) + return(Transport_Horse); + if(!strcmp(transport,"hgv")) + return(Transport_HGV); + break; + + case 'm': + if(!strcmp(transport,"moped")) + return(Transport_Moped); + if(!strcmp(transport,"motorbike")) + return(Transport_Motorbike); + if(!strcmp(transport,"motorcar")) + return(Transport_Motorcar); + break; + + case 'p': + if(!strcmp(transport,"psv")) + return(Transport_PSV); + break; + + case 'w': + if(!strcmp(transport,"wheelchair")) + return(Transport_Wheelchair); + break; + + default: + return(Transport_None); + } + + return(Transport_None); +} + + +/*++++++++++++++++++++++++++++++++++++++ + Decide on the type of property given the name of it. + + Property PropertyType Returns the type of the property. + + const char *property The string containing the method of property. + ++++++++++++++++++++++++++++++++++++++*/ + +Property PropertyType(const char *property) +{ + switch(*property) + { + case 'b': + if(!strcmp(property,"bridge")) + return(Property_Bridge); + break; + + case 'm': + if(!strcmp(property,"multilane")) + return(Property_Multilane); + break; + + case 'p': + if(!strcmp(property,"paved")) + return(Property_Paved); + break; + + case 't': + if(!strcmp(property,"tunnel")) + return(Property_Tunnel); + break; + + default: + return(Property_None); + } + + return(Property_None); +} + + +/*++++++++++++++++++++++++++++++++++++++ + A string containing the name of a type of highway. + + const char *HighwayName Returns the name. + + Highway highway The highway type. + ++++++++++++++++++++++++++++++++++++++*/ + +const char *HighwayName(Highway highway) +{ + switch(highway) + { + case Way_Motorway: + return("motorway"); + case Way_Trunk: + return("trunk"); + case Way_Primary: + return("primary"); + case Way_Secondary: + return("secondary"); + case Way_Tertiary: + return("tertiary"); + case Way_Unclassified: + return("unclassified"); + case Way_Residential: + return("residential"); + case Way_Service: + return("service"); + case Way_Track: + return("track"); + case Way_Cycleway: + return("cycleway"); + case Way_Path: + return("path"); + case Way_Steps: + return("steps"); + + case Way_Count: + ; + + case Way_OneWay: + case Way_Roundabout: + ; + } + + return(NULL); +} + + +/*++++++++++++++++++++++++++++++++++++++ + A string containing the name of a type of transport. + + const char *TransportName Returns the name. + + Transport transport The transport type. + ++++++++++++++++++++++++++++++++++++++*/ + +const char *TransportName(Transport transport) +{ + switch(transport) + { + case Transport_None: + return("NONE"); + + case Transport_Foot: + return("foot"); + case Transport_Horse: + return("horse"); + case Transport_Wheelchair: + return("wheelchair"); + case Transport_Bicycle: + return("bicycle"); + case Transport_Moped: + return("moped"); + case Transport_Motorbike: + return("motorbike"); + case Transport_Motorcar: + return("motorcar"); + case Transport_Goods: + return("goods"); + case Transport_HGV: + return("hgv"); + case Transport_PSV: + return("psv"); + + case Transport_Count: + ; + } + + return(NULL); +} + + +/*++++++++++++++++++++++++++++++++++++++ + A string containing the name of a highway property. + + const char *PropertyName Returns the name. + + Property property The property type. + ++++++++++++++++++++++++++++++++++++++*/ + +const char *PropertyName(Property property) +{ + switch(property) + { + case Property_None: + return("NONE"); + + case Property_Paved: + return("paved"); + + case Property_Multilane: + return("multilane"); + + case Property_Bridge: + return("bridge"); + + case Property_Tunnel: + return("tunnel"); + + case Property_Count: + ; + } + + return(NULL); +} + + +/*++++++++++++++++++++++++++++++++++++++ + A string containing the names of allowed transports on a way. + + const char *AllowedNameList Returns the list of names. + + wayallow_t allowed The allowed type. + ++++++++++++++++++++++++++++++++++++++*/ + +const char *AllowedNameList(wayallow_t allowed) +{ + static char string[256]; + + string[0]=0; + + if(allowed & Allow_Foot) + strcat(string,"foot"); + + if(allowed & Allow_Horse) + { + if(*string) strcat(string,", "); + strcat(string,"horse"); + } + + if(allowed & Allow_Wheelchair) + { + if(*string) strcat(string,", "); + strcat(string,"wheelchair"); + } + + if(allowed & Allow_Bicycle) + { + if(*string) strcat(string,", "); + strcat(string,"bicycle"); + } + + if(allowed & Allow_Moped) + { + if(*string) strcat(string,", "); + strcat(string,"moped"); + } + + if(allowed & Allow_Motorbike) + { + if(*string) strcat(string,", "); + strcat(string,"motorbike"); + } + + if(allowed & Allow_Motorcar) + { + if(*string) strcat(string,", "); + strcat(string,"motorcar"); + } + + if(allowed & Allow_Goods) + { + if(*string) strcat(string,", "); + strcat(string,"goods"); + } + + if(allowed & Allow_HGV) + { + if(*string) strcat(string,", "); + strcat(string,"hgv"); + } + + if(allowed & Allow_PSV) + { + if(*string) strcat(string,", "); + strcat(string,"psv"); + } + + return(string); +} + + +/*++++++++++++++++++++++++++++++++++++++ + A string containing the names of the properties of a way. + + const char *PropertiesNameList Returns the list of names. + + wayprop_t properties The properties of the way. + ++++++++++++++++++++++++++++++++++++++*/ + +const char *PropertiesNameList(wayprop_t properties) +{ + static char string[256]; + + string[0]=0; + + if(properties & Properties_Paved) + { + if(*string) strcat(string,", "); + strcat(string,"paved"); + } + + if(properties & Properties_Multilane) + { + if(*string) strcat(string,", "); + strcat(string,"multilane"); + } + + if(properties & Properties_Bridge) + { + if(*string) strcat(string,", "); + strcat(string,"bridge"); + } + + if(properties & Properties_Tunnel) + { + if(*string) strcat(string,", "); + strcat(string,"tunnel"); + } + + return(string); +} + + +/*++++++++++++++++++++++++++++++++++++++ + Returns a list of all the highway types. + + const char *HighwayList Return a list of all the highway types. + ++++++++++++++++++++++++++++++++++++++*/ + +const char *HighwayList(void) +{ + return " motorway = Motorway\n" + " trunk = Trunk\n" + " primary = Primary\n" + " secondary = Secondary\n" + " tertiary = Tertiary\n" + " unclassified = Unclassified\n" + " residential = Residential\n" + " service = Service\n" + " track = Track\n" + " cycleway = Cycleway\n" + " path = Path\n" + " steps = Steps\n" + ; +} + + +/*++++++++++++++++++++++++++++++++++++++ + Returns a list of all the transport types. + + const char *TransportList Return a list of all the transport types. + ++++++++++++++++++++++++++++++++++++++*/ + +const char *TransportList(void) +{ + return " foot = Foot\n" + " bicycle = Bicycle\n" + " wheelchair = Wheelchair\n" + " horse = Horse\n" + " moped = Moped (Small motorbike, limited speed)\n" + " motorbike = Motorbike\n" + " motorcar = Motorcar\n" + " goods = Goods (Small lorry, van)\n" + " hgv = HGV (Heavy Goods Vehicle - large lorry)\n" + " psv = PSV (Public Service Vehicle - bus, coach)\n" + ; +} + + +/*++++++++++++++++++++++++++++++++++++++ + Returns a list of all the property types. + + const char *PropertyList Return a list of all the highway proprties. + ++++++++++++++++++++++++++++++++++++++*/ + +const char *PropertyList(void) +{ + return " paved = Paved (suitable for normal wheels)\n" + " multilane = Multiple lanes\n" + " bridge = Bridge\n" + " Tunnel = Tunnel\n" + ; +} diff --git a/src/types.h b/src/types.h new file mode 100644 index 0000000..852122f --- /dev/null +++ b/src/types.h @@ -0,0 +1,350 @@ +/*************************************** + $Header: /home/amb/routino/src/RCS/types.h,v 1.40 2010/05/27 17:25:23 amb Exp $ + + Type definitions + + Part of the Routino routing software. + ******************/ /****************** + This file Copyright 2008-2010 Andrew M. Bishop + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + ***************************************/ + + +#ifndef TYPES_H +#define TYPES_H /*+ To stop multiple inclusions. +*/ + +#include +#include + + +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif + + +/* Constants and macros for handling them */ + + +/*+ The latitude and longitude conversion factor from floating point (radians) to integer. +*/ +#define LAT_LONG_SCALE (1024*65536) + +/*+ The latitude and longitude integer range within each bin. +*/ +#define LAT_LONG_BIN 65536 + + +/*+ A flag to mark a node as a super-node. +*/ +#define NODE_SUPER ((index_t)0x80000000) + +/*+ A segment index excluding the super-node flag. +*/ +#define SEGMENT(xxx) (index_t)((xxx)&(~NODE_SUPER)) + + +/*+ An undefined node index. +*/ +#define NO_NODE (~(index_t)0) + +/*+ An undefined segment index. +*/ +#define NO_SEGMENT (~(index_t)0) + +/*+ An undefined way index. +*/ +#define NO_WAY (~(index_t)0) + + +/*+ A flag to mark a segment as one-way from node1 to node2. +*/ +#define ONEWAY_1TO2 ((distance_t)0x80000000) + +/*+ A flag to mark a segment as one-way node2 to node1. +*/ +#define ONEWAY_2TO1 ((distance_t)0x40000000) + +/*+ A flag to mark a segment as a super-segment. +*/ +#define SEGMENT_SUPER ((distance_t)0x20000000) + +/*+ A flag to mark a segment as a normal segment. +*/ +#define SEGMENT_NORMAL ((distance_t)0x10000000) + +/*+ The real distance ignoring the ONEWAY_* and SEGMENT_* flags. +*/ +#define DISTANCE(xx) ((distance_t)(xx)&(~(ONEWAY_1TO2|ONEWAY_2TO1|SEGMENT_SUPER|SEGMENT_NORMAL))) + +/*+ The distance flags selecting only the ONEWAY_* and SEGMENT_* flags. +*/ +#define DISTFLAG(xx) ((distance_t)(xx)&(ONEWAY_1TO2|ONEWAY_2TO1|SEGMENT_SUPER|SEGMENT_NORMAL)) + + +/*+ A very large almost infinite distance. +*/ +#define INF_DISTANCE DISTANCE(~0) + +/*+ A very large almost infinite score. +*/ +#define INF_SCORE (score_t)1E30 + + +/* Simple Types */ + + +/*+ A node, segment or way index. +*/ +typedef uint32_t index_t; + + +/*+ A node latitude or longitude. +*/ +typedef int32_t latlong_t; + +/*+ A node latitude or longitude bin number. +*/ +typedef int16_t ll_bin_t; + +/*+ A node latitude or longitude offset. +*/ +typedef uint16_t ll_off_t; + + +/*+ Conversion from a latlong (integer latitude or longitude) to a bin number. +*/ +#define latlong_to_bin(xxx) (ll_bin_t)((latlong_t)((xxx)&~(LAT_LONG_BIN-1))/LAT_LONG_BIN) + +/*+ Conversion from a bin number to a latlong (integer latitude or longitude). +*/ +#define bin_to_latlong(xxx) ((latlong_t)(xxx)*LAT_LONG_BIN) + +/*+ Conversion from a latlong (integer latitude or longitude) to a bin offset. +*/ +#define latlong_to_off(xxx) (ll_off_t)((latlong_t)(xxx)&(LAT_LONG_BIN-1)) + +/*+ Conversion from a bin offset to a latlong (integer latitude or longitude). +*/ +#define off_to_latlong(xxx) ((latlong_t)(xxx)) + + +/*+ Conversion from a latitude or longitude in radians to a latlong (integer latitude or longitude). +*/ +#define radians_to_latlong(xxx) ((latlong_t)floor((xxx)*LAT_LONG_SCALE)) + +/*+ Conversion from a latlong (integer latitude or longitude) to a latitude or longitude in radians. +*/ +#define latlong_to_radians(xxx) ((double)(xxx)/LAT_LONG_SCALE) + + +/*+ Conversion from radians to degrees. +*/ +#define radians_to_degrees(xxx) ((xxx)*(180.0/M_PI)) + +/*+ Conversion from degrees to radians. +*/ +#define degrees_to_radians(xxx) ((xxx)*(M_PI/180.0)) + + +/*+ A distance, measured in metres. +*/ +typedef uint32_t distance_t; + +/*+ A duration, measured in 1/10th seconds. +*/ +typedef uint32_t duration_t; + +/*+ A routing optimisation score. +*/ +typedef float score_t; + + +/*+ Conversion from distance_t to kilometres. +*/ +#define distance_to_km(xx) ((double)(xx)/1000.0) + +/*+ Conversion from metres to distance_t. +*/ +#define km_to_distance(xx) ((distance_t)((double)(xx)*1000.0)) + +/*+ Conversion from duration_t to minutes. +*/ +#define duration_to_minutes(xx) ((double)(xx)/600.0) + +/*+ Conversion from duration_t to hours. +*/ +#define duration_to_hours(xx) ((double)(xx)/36000.0) + +/*+ Conversion from hours to duration_t. +*/ +#define hours_to_duration(xx) ((duration_t)((double)(xx)*36000.0)) + +/*+ Conversion from distance_t and speed_t to duration_t. +*/ +#define distance_speed_to_duration(xx,yy) ((duration_t)(((double)(xx)/(double)(yy))*(36000.0/1000.0))) + + +/*+ The type of a way. +*/ +typedef uint8_t waytype_t; + +/*+ The different types of a way. +*/ +typedef enum _Highway + { + Way_Motorway = 1, + Way_Trunk = 2, + Way_Primary = 3, + Way_Secondary = 4, + Way_Tertiary = 5, + Way_Unclassified= 6, + Way_Residential = 7, + Way_Service = 8, + Way_Track = 9, + Way_Cycleway =10, + Way_Path =11, + Way_Steps =12, + + Way_Count =13, /* One more than the number of highway types. */ + + Way_OneWay =32, + Way_Roundabout =64 + } + Highway; + +#define HIGHWAY(xx) ((xx)&0x1f) + + +/*+ The different methods of transport. +*/ +typedef enum _Transport + { + Transport_None = 0, + + Transport_Foot = 1, + Transport_Horse = 2, + Transport_Wheelchair = 3, + Transport_Bicycle = 4, + Transport_Moped = 5, + Transport_Motorbike = 6, + Transport_Motorcar = 7, + Transport_Goods = 8, + Transport_HGV = 9, + Transport_PSV = 10, + + Transport_Count = 11 /*+ One more than the number of transport types. +*/ + } + Transport; + + +/*+ The allowed traffic on a way. +*/ +typedef uint16_t wayallow_t; + +#define ALLOWED(xx) (1<<((xx)-1)) + +/*+ The different allowed traffic on a way. +*/ +typedef enum _Allowed + { + Allow_Foot = ALLOWED(Transport_Foot ), + Allow_Horse = ALLOWED(Transport_Horse ), + Allow_Wheelchair = ALLOWED(Transport_Wheelchair), + Allow_Bicycle = ALLOWED(Transport_Bicycle ), + Allow_Moped = ALLOWED(Transport_Moped ), + Allow_Motorbike = ALLOWED(Transport_Motorbike ), + Allow_Motorcar = ALLOWED(Transport_Motorcar ), + Allow_Goods = ALLOWED(Transport_Goods ), + Allow_HGV = ALLOWED(Transport_HGV ), + Allow_PSV = ALLOWED(Transport_PSV ), + + Allow_ALL = 65535 + } + Allowed; + + +/*+ The individual properties of a highway. +*/ +typedef enum _Property + { + Property_None = 0, + + Property_Paved = 1, + Property_Multilane = 2, + Property_Bridge = 3, + Property_Tunnel = 4, + + Property_Count = 5 /* One more than the number of property types. */ + } + Property; + + +/*+ The combined set of properties of a way. +*/ +typedef uint8_t wayprop_t; + +#define PROPERTIES(xx) (1<<((xx)-1)) + +/*+ The different properties of a way. +*/ +typedef enum _Properties + { + Properties_Paved = PROPERTIES(Property_Paved), + Properties_Multilane = PROPERTIES(Property_Multilane), + Properties_Bridge = PROPERTIES(Property_Bridge), + Properties_Tunnel = PROPERTIES(Property_Tunnel), + + Properties_ALL = 255 + } + Properties; + + +/*+ The speed limit of a way, measured in km/hour. +*/ +typedef uint8_t speed_t; + +/*+ The maximum weight of a way, measured in 0.2 tonnes. +*/ +typedef uint8_t weight_t; + +/*+ The maximum height of a way, measured in 0.1 metres. +*/ +typedef uint8_t height_t; + +/*+ The maximum width of a way, measured in 0.1 metres. +*/ +typedef uint8_t width_t; + +/*+ The maximum length of a way, measured in 0.1 metres. +*/ +typedef uint8_t length_t; + + +/*+ Conversion of km/hr to speed_t. +*/ +#define kph_to_speed(xxx) (speed_t)(xxx) + +/*+ Conversion of speed_t to km/hr. +*/ +#define speed_to_kph(xxx) (int)(xxx) + +/*+ Conversion of tonnes to weight_t. +*/ +#define tonnes_to_weight(xxx) (weight_t)((xxx)*5) + +/*+ Conversion of weight_t to tonnes. +*/ +#define weight_to_tonnes(xxx) ((double)(xxx)/5.0) + +/*+ Conversion of metres to height_t. +*/ +#define metres_to_height(xxx) (height_t)((xxx)*10) + +/*+ Conversion of height_t to metres. +*/ +#define height_to_metres(xxx) ((double)(xxx)/10.0) + +/*+ Conversion of metres to width_t. +*/ +#define metres_to_width(xxx) (width_t)((xxx)*10) + +/*+ Conversion of width_t to metres. +*/ +#define width_to_metres(xxx) ((double)(xxx)/10.0) + +/*+ Conversion of metres to length_t. +*/ +#define metres_to_length(xxx) (length_t)((xxx)*10) + +/*+ Conversion of length_t to metres. +*/ +#define length_to_metres(xxx) ((double)(xxx)/10.0) + + +/* Data structures */ + +typedef struct _Node Node; + +typedef struct _Nodes Nodes; + +typedef struct _Segment Segment; + +typedef struct _Segments Segments; + +typedef struct _Way Way; + +typedef struct _Ways Ways; + + +/* Functions */ + +Highway HighwayType(const char *highway); +Transport TransportType(const char *transport); +Property PropertyType(const char *property); + +const char *HighwayName(Highway highway); +const char *TransportName(Transport transport); +const char *PropertyName(Property property); + +const char *AllowedNameList(wayallow_t allowed); +const char *PropertiesNameList(wayprop_t properties); + +const char *HighwayList(void); +const char *TransportList(void); +const char *PropertyList(void); + + +#endif /* TYPES_H */ diff --git a/src/typesx.h b/src/typesx.h new file mode 100644 index 0000000..25a350d --- /dev/null +++ b/src/typesx.h @@ -0,0 +1,56 @@ +/*************************************** + $Header: /home/amb/routino/src/RCS/typesx.h,v 1.3 2009/10/09 18:47:40 amb Exp $ + + Type definitions for eXtended types. + + Part of the Routino routing software. + ******************/ /****************** + This file Copyright 2008,2009 Andrew M. Bishop + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + ***************************************/ + + +#ifndef TYPESX_H +#define TYPESX_H /*+ To stop multiple inclusions. +*/ + + +#include + + +/* Simple Types */ + +/*+ A node identifier - must be at least as large as index_t. +*/ +typedef uint32_t node_t; + +/*+ A way identifier - must be at least as large as index_t. +*/ +typedef uint32_t way_t; + + +/* Data structures */ + +typedef struct _NodeX NodeX; + +typedef struct _NodesX NodesX; + +typedef struct _SegmentX SegmentX; + +typedef struct _SegmentsX SegmentsX; + +typedef struct _WayX WayX; + +typedef struct _WaysX WaysX; + + +#endif /* TYPESX_H */ diff --git a/src/visualiser.c b/src/visualiser.c new file mode 100644 index 0000000..c4a5da4 --- /dev/null +++ b/src/visualiser.c @@ -0,0 +1,628 @@ +/*************************************** + $Header: /home/amb/routino/src/RCS/visualiser.c,v 1.7 2009/07/09 18:34:38 amb Exp $ + + Extract data from Routino. + + Part of the Routino routing software. + ******************/ /****************** + This file Copyright 2008,2009 Andrew M. Bishop + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + ***************************************/ + + +#include +#include +#include + +#include "types.h" +#include "visualiser.h" +#include "nodes.h" +#include "segments.h" +#include "ways.h" + + +#define SPEED_LIMIT 1 +#define WEIGHT_LIMIT 2 +#define HEIGHT_LIMIT 3 +#define WIDTH_LIMIT 4 +#define LENGTH_LIMIT 5 + +/* Local types */ + +typedef void (*callback_t)(index_t node,double latitude,double longitude); + +/* Local variables */ + +static Nodes *OSMNodes; +static Segments *OSMSegments; +static Ways *OSMWays; + +static double LatMin; +static double LatMax; +static double LonMin; +static double LonMax; + +static int limit_type=0; + +/* Local functions */ + +static void find_all_nodes(Nodes *nodes,callback_t callback); +static void output_junctions(index_t node,double latitude,double longitude); +static void output_super(index_t node,double latitude,double longitude); +static void output_oneway(index_t node,double latitude,double longitude); +static void output_limits(index_t node,double latitude,double longitude); + + +/*++++++++++++++++++++++++++++++++++++++ + Output the data for junctions. + + Nodes *nodes The set of nodes to use. + + Segments *segments The set of segments to use. + + Ways *ways The set of ways to use. + + double latmin The minimum latitude. + + double latmax The maximum latitude. + + double lonmin The minimum longitude. + + double lonmax The maximum longitude. + ++++++++++++++++++++++++++++++++++++++*/ + +void OutputJunctions(Nodes *nodes,Segments *segments,Ways *ways,double latmin,double latmax,double lonmin,double lonmax) +{ + /* Use local variables so that the callback doesn't need to pass them backwards and forwards */ + + OSMNodes=nodes; + OSMSegments=segments; + OSMWays=ways; + + LatMin=latmin; + LatMax=latmax; + LonMin=lonmin; + LonMax=lonmax; + + /* Iterate through the nodes and process them */ + + find_all_nodes(nodes,(callback_t)output_junctions); +} + + +/*++++++++++++++++++++++++++++++++++++++ + Process a single node (called as a callback). + + index_t node The node to output. + + double latitude The latitude of the node. + + double longitude The longitude of the node. + ++++++++++++++++++++++++++++++++++++++*/ + +static void output_junctions(index_t node,double latitude,double longitude) +{ + Segment *segment; + Way *firstway; + int count=0,difference=0; + + segment=FirstSegment(OSMSegments,OSMNodes,node); + firstway=LookupWay(OSMWays,segment->way); + + do + { + Way *way=LookupWay(OSMWays,segment->way); + + if(IsNormalSegment(segment)) + count++; + + if(WaysCompare(firstway,way)) + difference=1; + + segment=NextSegment(OSMSegments,segment,node); + } + while(segment); + + if(count!=2 || difference) + printf("%.6f %.6f %d\n",radians_to_degrees(latitude),radians_to_degrees(longitude),count); +} + + +/*++++++++++++++++++++++++++++++++++++++ + Output the data for super-nodes and super-segments. + + Nodes *nodes The set of nodes to use. + + Segments *segments The set of segments to use. + + Ways *ways The set of ways to use. + + double latmin The minimum latitude. + + double latmax The maximum latitude. + + double lonmin The minimum longitude. + + double lonmax The maximum longitude. + ++++++++++++++++++++++++++++++++++++++*/ + +void OutputSuper(Nodes *nodes,Segments *segments,Ways *ways,double latmin,double latmax,double lonmin,double lonmax) +{ + /* Use local variables so that the callback doesn't need to pass them backwards and forwards */ + + OSMNodes=nodes; + OSMSegments=segments; + OSMWays=ways; + + LatMin=latmin; + LatMax=latmax; + LonMin=lonmin; + LonMax=lonmax; + + /* Iterate through the nodes and process them */ + + find_all_nodes(nodes,(callback_t)output_super); +} + + +/*++++++++++++++++++++++++++++++++++++++ + Process a single node (called as a callback). + + index_t node The node to output. + + double latitude The latitude of the node. + + double longitude The longitude of the node. + ++++++++++++++++++++++++++++++++++++++*/ + +static void output_super(index_t node,double latitude,double longitude) +{ + Segment *segment; + + if(!IsSuperNode(OSMNodes,node)) + return; + + printf("%.6f %.6f n\n",radians_to_degrees(latitude),radians_to_degrees(longitude)); + + segment=FirstSegment(OSMSegments,OSMNodes,node); + + do + { + if(IsSuperSegment(segment)) + { + index_t othernode=OtherNode(segment,node); + double lat,lon; + + GetLatLong(OSMNodes,othernode,&lat,&lon); + + if(node>othernode || (latLatMax || lonLonMax)) + printf("%.6f %.6f s\n",radians_to_degrees(lat),radians_to_degrees(lon)); + } + + segment=NextSegment(OSMSegments,segment,node); + } + while(segment); +} + + +/*++++++++++++++++++++++++++++++++++++++ + Output the data for one-way segments. + + Nodes *nodes The set of nodes to use. + + Segments *segments The set of segments to use. + + Ways *ways The set of ways to use. + + double latmin The minimum latitude. + + double latmax The maximum latitude. + + double lonmin The minimum longitude. + + double lonmax The maximum longitude. + ++++++++++++++++++++++++++++++++++++++*/ + +void OutputOneway(Nodes *nodes,Segments *segments,Ways *ways,double latmin,double latmax,double lonmin,double lonmax) +{ + /* Use local variables so that the callback doesn't need to pass them backwards and forwards */ + + OSMNodes=nodes; + OSMSegments=segments; + OSMWays=ways; + + LatMin=latmin; + LatMax=latmax; + LonMin=lonmin; + LonMax=lonmax; + + /* Iterate through the nodes and process them */ + + find_all_nodes(nodes,(callback_t)output_oneway); +} + + +/*++++++++++++++++++++++++++++++++++++++ + Process a single node (called as a callback). + + index_t node The node to output. + + double latitude The latitude of the node. + + double longitude The longitude of the node. + ++++++++++++++++++++++++++++++++++++++*/ + +static void output_oneway(index_t node,double latitude,double longitude) +{ + Segment *segment; + + segment=FirstSegment(OSMSegments,OSMNodes,node); + + do + { + if(IsNormalSegment(segment)) + { + index_t othernode=OtherNode(segment,node); + + if(node>othernode) + { + double lat,lon; + + GetLatLong(OSMNodes,othernode,&lat,&lon); + + if(IsOnewayFrom(segment,node)) + printf("%.6f %.6f %.6f %.6f\n",radians_to_degrees(latitude),radians_to_degrees(longitude),radians_to_degrees(lat),radians_to_degrees(lon)); + else if(IsOnewayFrom(segment,othernode)) + printf("%.6f %.6f %.6f %.6f\n",radians_to_degrees(lat),radians_to_degrees(lon),radians_to_degrees(latitude),radians_to_degrees(longitude)); + } + } + + segment=NextSegment(OSMSegments,segment,node); + } + while(segment); +} + + +/*++++++++++++++++++++++++++++++++++++++ + Output the data for speed limits. + + Nodes *nodes The set of nodes to use. + + Segments *segments The set of segments to use. + + Ways *ways The set of ways to use. + + double latmin The minimum latitude. + + double latmax The maximum latitude. + + double lonmin The minimum longitude. + + double lonmax The maximum longitude. + ++++++++++++++++++++++++++++++++++++++*/ + +void OutputSpeedLimits(Nodes *nodes,Segments *segments,Ways *ways,double latmin,double latmax,double lonmin,double lonmax) +{ + /* Use local variables so that the callback doesn't need to pass them backwards and forwards */ + + OSMNodes=nodes; + OSMSegments=segments; + OSMWays=ways; + + LatMin=latmin; + LatMax=latmax; + LonMin=lonmin; + LonMax=lonmax; + + /* Iterate through the nodes and process them */ + + limit_type=SPEED_LIMIT; + + find_all_nodes(nodes,(callback_t)output_limits); +} + + +/*++++++++++++++++++++++++++++++++++++++ + Output the data for weight limits. + + Nodes *nodes The set of nodes to use. + + Segments *segments The set of segments to use. + + Ways *ways The set of ways to use. + + double latmin The minimum latitude. + + double latmax The maximum latitude. + + double lonmin The minimum longitude. + + double lonmax The maximum longitude. + ++++++++++++++++++++++++++++++++++++++*/ + +void OutputWeightLimits(Nodes *nodes,Segments *segments,Ways *ways,double latmin,double latmax,double lonmin,double lonmax) +{ + /* Use local variables so that the callback doesn't need to pass them backwards and forwards */ + + OSMNodes=nodes; + OSMSegments=segments; + OSMWays=ways; + + LatMin=latmin; + LatMax=latmax; + LonMin=lonmin; + LonMax=lonmax; + + /* Iterate through the nodes and process them */ + + limit_type=WEIGHT_LIMIT; + + find_all_nodes(nodes,(callback_t)output_limits); +} + + +/*++++++++++++++++++++++++++++++++++++++ + Output the data for height limits. + + Nodes *nodes The set of nodes to use. + + Segments *segments The set of segments to use. + + Ways *ways The set of ways to use. + + double latmin The minimum latitude. + + double latmax The maximum latitude. + + double lonmin The minimum longitude. + + double lonmax The maximum longitude. + ++++++++++++++++++++++++++++++++++++++*/ + +void OutputHeightLimits(Nodes *nodes,Segments *segments,Ways *ways,double latmin,double latmax,double lonmin,double lonmax) +{ + /* Use local variables so that the callback doesn't need to pass them backwards and forwards */ + + OSMNodes=nodes; + OSMSegments=segments; + OSMWays=ways; + + LatMin=latmin; + LatMax=latmax; + LonMin=lonmin; + LonMax=lonmax; + + /* Iterate through the nodes and process them */ + + limit_type=HEIGHT_LIMIT; + + find_all_nodes(nodes,(callback_t)output_limits); +} + + +/*++++++++++++++++++++++++++++++++++++++ + Output the data for width limits. + + Nodes *nodes The set of nodes to use. + + Segments *segments The set of segments to use. + + Ways *ways The set of ways to use. + + double latmin The minimum latitude. + + double latmax The maximum latitude. + + double lonmin The minimum longitude. + + double lonmax The maximum longitude. + ++++++++++++++++++++++++++++++++++++++*/ + +void OutputWidthLimits(Nodes *nodes,Segments *segments,Ways *ways,double latmin,double latmax,double lonmin,double lonmax) +{ + /* Use local variables so that the callback doesn't need to pass them backwards and forwards */ + + OSMNodes=nodes; + OSMSegments=segments; + OSMWays=ways; + + LatMin=latmin; + LatMax=latmax; + LonMin=lonmin; + LonMax=lonmax; + + /* Iterate through the nodes and process them */ + + limit_type=WIDTH_LIMIT; + + find_all_nodes(nodes,(callback_t)output_limits); +} + + +/*++++++++++++++++++++++++++++++++++++++ + Output the data for length limits. + + Nodes *nodes The set of nodes to use. + + Segments *segments The set of segments to use. + + Ways *ways The set of ways to use. + + double latmin The minimum latitude. + + double latmax The maximum latitude. + + double lonmin The minimum longitude. + + double lonmax The maximum longitude. + ++++++++++++++++++++++++++++++++++++++*/ + +void OutputLengthLimits(Nodes *nodes,Segments *segments,Ways *ways,double latmin,double latmax,double lonmin,double lonmax) +{ + /* Use local variables so that the callback doesn't need to pass them backwards and forwards */ + + OSMNodes=nodes; + OSMSegments=segments; + OSMWays=ways; + + LatMin=latmin; + LatMax=latmax; + LonMin=lonmin; + LonMax=lonmax; + + /* Iterate through the nodes and process them */ + + limit_type=LENGTH_LIMIT; + + find_all_nodes(nodes,(callback_t)output_limits); +} + + +/*++++++++++++++++++++++++++++++++++++++ + Process a single node (called as a callback). + + index_t node The node to output. + + double latitude The latitude of the node. + + double longitude The longitude of the node. + ++++++++++++++++++++++++++++++++++++++*/ + +static void output_limits(index_t node,double latitude,double longitude) +{ + Segment *segment,*segments[16]; + Way *ways[16]; + int limits[16]; + int count=0; + int i,j,same=0; + + segment=FirstSegment(OSMSegments,OSMNodes,node); + + do + { + if(IsNormalSegment(segment) && count<16) + { + ways [count]=LookupWay(OSMWays,segment->way); + segments[count]=segment; + + switch(limit_type) + { + case SPEED_LIMIT: limits[count]=ways[count]->speed; break; + case WEIGHT_LIMIT: limits[count]=ways[count]->weight; break; + case HEIGHT_LIMIT: limits[count]=ways[count]->height; break; + case WIDTH_LIMIT: limits[count]=ways[count]->width; break; + case LENGTH_LIMIT: limits[count]=ways[count]->length; break; + } + + if(limits[count] || ways[count]->typelatzero; + int32_t latmaxbin=latlong_to_bin(radians_to_latlong(LatMax))-nodes->latzero; + int32_t lonminbin=latlong_to_bin(radians_to_latlong(LonMin))-nodes->lonzero; + int32_t lonmaxbin=latlong_to_bin(radians_to_latlong(LonMax))-nodes->lonzero; + int latb,lonb,llbin; + index_t node; + + /* Loop through all of the nodes. */ + + for(latb=latminbin;latb<=latmaxbin;latb++) + for(lonb=lonminbin;lonb<=lonmaxbin;lonb++) + { + llbin=lonb*nodes->latbins+latb; + + if(llbin<0 || llbin>(nodes->latbins*nodes->lonbins)) + continue; + + for(node=nodes->offsets[llbin];nodeoffsets[llbin+1];node++) + { + double lat=latlong_to_radians(bin_to_latlong(nodes->latzero+latb)+off_to_latlong(nodes->nodes[node].latoffset)); + double lon=latlong_to_radians(bin_to_latlong(nodes->lonzero+lonb)+off_to_latlong(nodes->nodes[node].lonoffset)); + + if(lat>LatMin && latLonMin && lon. + ***************************************/ + + +#ifndef VISUALISER_H +#define VISUALISER_H /*+ To stop multiple inclusions. +*/ + +#include "types.h" + + +/* In visualiser.c */ + +void OutputJunctions(Nodes *nodes,Segments *segments,Ways *ways,double latmin,double latmax,double lonmin,double lonmax); + +void OutputSuper(Nodes *nodes,Segments *segments,Ways *ways,double latmin,double latmax,double lonmin,double lonmax); + +void OutputOneway(Nodes *nodes,Segments *segments,Ways *ways,double latmin,double latmax,double lonmin,double lonmax); + +void OutputSpeedLimits(Nodes *nodes,Segments *segments,Ways *ways,double latmin,double latmax,double lonmin,double lonmax); + +void OutputWeightLimits(Nodes *nodes,Segments *segments,Ways *ways,double latmin,double latmax,double lonmin,double lonmax); + +void OutputHeightLimits(Nodes *nodes,Segments *segments,Ways *ways,double latmin,double latmax,double lonmin,double lonmax); +void OutputWidthLimits(Nodes *nodes,Segments *segments,Ways *ways,double latmin,double latmax,double lonmin,double lonmax); +void OutputLengthLimits(Nodes *nodes,Segments *segments,Ways *ways,double latmin,double latmax,double lonmin,double lonmax); + + +#endif /* VISUALISER_H */ diff --git a/src/ways.c b/src/ways.c new file mode 100644 index 0000000..3142415 --- /dev/null +++ b/src/ways.c @@ -0,0 +1,103 @@ +/*************************************** + $Header: /home/amb/routino/src/RCS/ways.c,v 1.44 2010/04/28 17:27:02 amb Exp $ + + Way data type functions. + + Part of the Routino routing software. + ******************/ /****************** + This file Copyright 2008-2010 Andrew M. Bishop + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + ***************************************/ + + +#include + +#include "functions.h" +#include "ways.h" + + +/*++++++++++++++++++++++++++++++++++++++ + Load in a way list from a file. + + Ways* LoadWayList Returns the way list. + + const char *filename The name of the file to load. + ++++++++++++++++++++++++++++++++++++++*/ + +Ways *LoadWayList(const char *filename) +{ + void *data; + Ways *ways; + + ways=(Ways*)malloc(sizeof(Ways)); + + data=MapFile(filename); + + /* Copy the Ways structure from the loaded data */ + + *ways=*((Ways*)data); + + /* Adjust the pointers in the Ways structure. */ + + ways->data =data; + ways->ways =(Way *)(data+sizeof(Ways)); + ways->names=(char*)(data+(sizeof(Ways)+ways->number*sizeof(Way))); + + return(ways); +} + + +/*++++++++++++++++++++++++++++++++++++++ + Return 0 if the two ways are the same (in respect of their types and limits), + otherwise return positive or negative to allow sorting. + + int WaysCompare Returns a comparison. + + Way *way1 The first way. + + Way *way2 The second way. + ++++++++++++++++++++++++++++++++++++++*/ + +int WaysCompare(Way *way1,Way *way2) +{ + if(way1==way2) + return(0); + + if(way1->type!=way2->type) + return((int)way1->type - (int)way2->type); + + if(way1->allow!=way2->allow) + return((int)way1->allow - (int)way2->allow); + + if(way1->props!=way2->props) + return((int)way1->props - (int)way2->props); + + if(way1->speed!=way2->speed) + return((int)way1->speed - (int)way2->speed); + + if(way1->weight!=way2->weight) + return((int)way1->weight - (int)way2->weight); + + if(way1->height!=way2->height) + return((int)way1->height - (int)way2->height); + + if(way1->width!=way2->width) + return((int)way1->width - (int)way2->width); + + if(way1->length!=way2->length) + return((int)way1->length - (int)way2->length); + + return(0); +} diff --git a/src/ways.h b/src/ways.h new file mode 100644 index 0000000..7a5a94a --- /dev/null +++ b/src/ways.h @@ -0,0 +1,96 @@ +/*************************************** + $Header: /home/amb/routino/src/RCS/ways.h,v 1.37 2010/05/29 13:54:24 amb Exp $ + + A header file for the ways. + + Part of the Routino routing software. + ******************/ /****************** + This file Copyright 2008-2010 Andrew M. Bishop + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + ***************************************/ + + +#ifndef WAYS_H +#define WAYS_H /*+ To stop multiple inclusions. +*/ + +#include + +#include "types.h" + + +/* Data structures */ + + +/*+ A structure containing a single way (members ordered to minimise overall size). +*/ +struct _Way +{ + index_t name; /*+ The offset of the name of the way in the names array. +*/ + + wayallow_t allow; /*+ The type of traffic allowed on the way. +*/ + + waytype_t type; /*+ The highway type of the way. +*/ + + wayprop_t props; /*+ The properties of the way. +*/ + + speed_t speed; /*+ The defined maximum speed limit of the way. +*/ + + weight_t weight; /*+ The defined maximum weight of traffic on the way. +*/ + height_t height; /*+ The defined maximum height of traffic on the way. +*/ + width_t width; /*+ The defined maximum width of traffic on the way. +*/ + length_t length; /*+ The defined maximum length of traffic on the way. +*/ +}; + + +/*+ A structure containing a set of ways (mmap format). +*/ +struct _Ways +{ + uint32_t number; /*+ How many ways are stored? +*/ + uint32_t onumber; /*+ How many ways were there originally? +*/ + + wayallow_t allow; /*+ The types of traffic that were seen when parsing. +*/ + wayprop_t props; /*+ The properties that were seen when parsing. +*/ + + Way *ways; /*+ An array of ways. +*/ + char *names; /*+ An array of characters containing the names. +*/ + + void *data; /*+ The memory mapped data. +*/ +}; + + +/* Macros */ + + +/*+ Return a Way* pointer given a set of ways and an index. +*/ +#define LookupWay(xxx,yyy) (&(xxx)->ways[yyy]) + +/*+ Return the raw name of a way given the Way pointer and a set of ways. +*/ +#define WayNameRaw(xxx,yyy) (&(xxx)->names[(yyy)->name]) + +/*+ Decide if a way has a name or not. +*/ +#define WayNamed(xxx,yyy) ((xxx)->names[(yyy)->name]) + +/*+ Return the name of a way if it has one or the name of the highway type otherwise. +*/ +#define WayNameHighway(xxx,yyy) (WayNamed(xxx,yyy)?WayNameRaw(xxx,yyy):HighwayName(HIGHWAY(yyy->type))) + + +/* Functions */ + + +Ways *LoadWayList(const char *filename); + +int WaysCompare(Way *way1,Way *way2); + + +#endif /* WAYS_H */ diff --git a/src/waysx.c b/src/waysx.c new file mode 100644 index 0000000..3c4635b --- /dev/null +++ b/src/waysx.c @@ -0,0 +1,623 @@ +/*************************************** + $Header: /home/amb/routino/src/RCS/waysx.c,v 1.38 2010/05/22 18:40:47 amb Exp $ + + Extended Way data type functions. + + Part of the Routino routing software. + ******************/ /****************** + This file Copyright 2008-2010 Andrew M. Bishop + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + ***************************************/ + + +#include +#include +#include +#include + +#include "functions.h" +#include "waysx.h" +#include "ways.h" + + +/* Variables */ + +/*+ The command line '--slim' option. +*/ +extern int option_slim; + +/*+ The command line '--tmpdir' option or its default value. +*/ +extern char *option_tmpdirname; + +/*+ A temporary file-local variable for use by the sort functions. +*/ +static WaysX *sortwaysx; + +/* Functions */ + +static int sort_by_name_and_prop_and_id(WayX *a,WayX *b); +static int deduplicate_by_id(WayX *wayx,index_t index); + +static int sort_by_id(WayX *a,WayX *b); +static int index_by_id(WayX *wayx,index_t index); + + +/*++++++++++++++++++++++++++++++++++++++ + Allocate a new way list (create a new file or open an existing one). + + WaysX *NewWayList Returns the way list. + + int append Set to 1 if the file is to be opened for appending (now or later). + ++++++++++++++++++++++++++++++++++++++*/ + +WaysX *NewWayList(int append) +{ + WaysX *waysx; + + waysx=(WaysX*)calloc(1,sizeof(WaysX)); + + assert(waysx); /* Check calloc() worked */ + + waysx->filename=(char*)malloc(strlen(option_tmpdirname)+32); + + if(append) + sprintf(waysx->filename,"%s/ways.input.tmp",option_tmpdirname); + else + sprintf(waysx->filename,"%s/ways.%p.tmp",option_tmpdirname,waysx); + + if(append) + { + off_t size,position=0; + + waysx->fd=AppendFile(waysx->filename); + + size=SizeFile(waysx->filename); + + while(positionfd,position); + ReadFile(waysx->fd,&waysize,FILESORT_VARSIZE); + + waysx->xnumber++; + position+=waysize+FILESORT_VARSIZE; + } + + SeekFile(waysx->fd,size); + } + else + waysx->fd=OpenFile(waysx->filename); + + waysx->nfilename=(char*)malloc(strlen(option_tmpdirname)+32); + sprintf(waysx->nfilename,"%s/waynames.%p.tmp",option_tmpdirname,waysx); + + return(waysx); +} + + +/*++++++++++++++++++++++++++++++++++++++ + Free a way list. + + WaysX *waysx The list to be freed. + + int keep Set to 1 if the file is to be kept. + ++++++++++++++++++++++++++++++++++++++*/ + +void FreeWayList(WaysX *waysx,int keep) +{ + if(!keep) + DeleteFile(waysx->filename); + + free(waysx->filename); + + if(waysx->idata) + free(waysx->idata); + + DeleteFile(waysx->nfilename); + + free(waysx->nfilename); + + free(waysx); +} + + +/*++++++++++++++++++++++++++++++++++++++ + Append a way to a way list. + + void AppendWay Returns the newly appended way. + + WaysX* waysx The set of ways to process. + + way_t id The ID of the way. + + Way *way The way data itself. + + const char *name The name or reference of the way. + ++++++++++++++++++++++++++++++++++++++*/ + +void AppendWay(WaysX* waysx,way_t id,Way *way,const char *name) +{ + WayX wayx; + FILESORT_VARINT size; + + assert(!waysx->idata); /* Must not have idata filled in => unsorted */ + + wayx.id=id; + wayx.prop=0; + wayx.way=*way; + + size=sizeof(WayX)+strlen(name)+1; + + WriteFile(waysx->fd,&size,FILESORT_VARSIZE); + WriteFile(waysx->fd,&wayx,sizeof(WayX)); + WriteFile(waysx->fd,name,strlen(name)+1); + + waysx->xnumber++; +} + + +/*++++++++++++++++++++++++++++++++++++++ + Sort the list of ways. + + WaysX* waysx The set of ways to process. + ++++++++++++++++++++++++++++++++++++++*/ + +void SortWayList(WaysX* waysx) +{ + index_t i; + int fd,nfd; + char *names[2]={NULL,NULL}; + int namelen[2]={0,0}; + int nnames=0,nprops=0; + uint32_t lastlength=0; + Way lastway; + + /* Check the start conditions */ + + assert(!waysx->idata); /* Must not have idata filled in => unsorted */ + + /* Print the start message */ + + printf("Sorting Ways"); + fflush(stdout); + + /* Close the file and re-open it (finished appending) */ + + CloseFile(waysx->fd); + waysx->fd=ReOpenFile(waysx->filename); + + DeleteFile(waysx->filename); + + fd=OpenFile(waysx->filename); + + /* Sort the ways to allow compacting them and remove duplicates */ + + sortwaysx=waysx; + + filesort_vary(waysx->fd,fd,(int (*)(const void*,const void*))sort_by_name_and_prop_and_id,(int (*)(void*,index_t))deduplicate_by_id); + + /* Close the files */ + + CloseFile(waysx->fd); + CloseFile(fd); + + /* Print the final message */ + + printf("\rSorted Ways: Ways=%d Duplicates=%d\n",waysx->xnumber,waysx->xnumber-waysx->number); + fflush(stdout); + + + /* Print the start message */ + + printf("Compacting Ways: Ways=0 Names=0 Properties=0"); + fflush(stdout); + + /* Open the files */ + + waysx->fd=ReOpenFile(waysx->filename); + + DeleteFile(waysx->filename); + + fd=OpenFile(waysx->filename); + nfd=OpenFile(waysx->nfilename); + + /* Copy from the single file into two files and index as we go. */ + + for(i=0;inumber;i++) + { + WayX wayx; + FILESORT_VARINT size; + + ReadFile(waysx->fd,&size,FILESORT_VARSIZE); + + if(namelen[nnames%2]fd,&wayx,sizeof(WayX)); + ReadFile(waysx->fd,names[nnames%2],size-sizeof(WayX)); + + if(nnames==0 || strcmp(names[0],names[1])) + { + WriteFile(nfd,names[nnames%2],size-sizeof(WayX)); + + lastlength=waysx->nlength; + waysx->nlength+=size-sizeof(WayX); + + nnames++; + } + + wayx.way.name=lastlength; + + if(nprops==0 || wayx.way.name!=lastway.name || WaysCompare(&lastway,&wayx.way)) + { + lastway=wayx.way; + + waysx->cnumber++; + + nprops++; + } + + wayx.prop=nprops-1; + + WriteFile(fd,&wayx,sizeof(WayX)); + + if(!((i+1)%10000)) + { + printf("\rCompacting Ways: Ways=%d Names=%d Properties=%d",i+1,nnames,nprops); + fflush(stdout); + } + } + + if(names[0]) free(names[0]); + if(names[1]) free(names[1]); + + /* Close the files */ + + CloseFile(waysx->fd); + CloseFile(fd); + + waysx->fd=ReOpenFile(waysx->filename); + + CloseFile(nfd); + + /* Print the final message */ + + printf("\rCompacted Ways: Ways=%d Names=%d Properties=%d \n",waysx->number,nnames,nprops); + fflush(stdout); + + + /* Print the start message */ + + printf("Sorting Ways"); + fflush(stdout); + + /* Open the files */ + + waysx->fd=ReOpenFile(waysx->filename); + + DeleteFile(waysx->filename); + + fd=OpenFile(waysx->filename); + + /* Allocate the array of indexes */ + + waysx->idata=(way_t*)malloc(waysx->number*sizeof(way_t)); + + assert(waysx->idata); /* Check malloc() worked */ + + /* Sort the ways by index and index them */ + + sortwaysx=waysx; + + filesort_fixed(waysx->fd,fd,sizeof(WayX),(int (*)(const void*,const void*))sort_by_id,(int (*)(void*,index_t))index_by_id); + + /* Close the files and re-open them */ + + CloseFile(waysx->fd); + CloseFile(fd); + + waysx->fd=ReOpenFile(waysx->filename); + + /* Print the final message */ + + printf("\rSorted Ways: Ways=%d\n",waysx->number); + fflush(stdout); +} + + +/*++++++++++++++++++++++++++++++++++++++ + Sort the ways into id order. + + int sort_by_id Returns the comparison of the id fields. + + WayX *a The first extended way. + + WayX *b The second extended way. + ++++++++++++++++++++++++++++++++++++++*/ + +static int sort_by_id(WayX *a,WayX *b) +{ + way_t a_id=a->id; + way_t b_id=b->id; + + if(a_idb_id) + return(1); + else + return(0); +} + + +/*++++++++++++++++++++++++++++++++++++++ + Sort the ways into name, properties and id order. + + int sort_by_name_and_prop_and_id Returns the comparison of the name, properties and id fields. + + WayX *a The first extended Way. + + WayX *b The second extended Way. + ++++++++++++++++++++++++++++++++++++++*/ + +static int sort_by_name_and_prop_and_id(WayX *a,WayX *b) +{ + int compare; + char *a_name=(char*)a+sizeof(WayX); + char *b_name=(char*)b+sizeof(WayX); + + compare=strcmp(a_name,b_name); + + if(compare) + return(compare); + + compare=WaysCompare(&a->way,&b->way); + + if(compare) + return(compare); + + return(sort_by_id(a,b)); +} + + +/*++++++++++++++++++++++++++++++++++++++ + Deduplicate the extended ways using the id after sorting. + + int deduplicate_by_id Return 1 if the value is to be kept, otherwise zero. + + WayX *wayx The extended way. + + index_t index The index of this way in the total. + ++++++++++++++++++++++++++++++++++++++*/ + +static int deduplicate_by_id(WayX *wayx,index_t index) +{ + static way_t previd; + + if(index==0 || wayx->id!=previd) + { + previd=wayx->id; + + sortwaysx->number++; + + return(1); + } + + return(0); +} + + +/*++++++++++++++++++++++++++++++++++++++ + Index the ways after sorting. + + int index_by_id Return 1 if the value is to be kept, otherwise zero. + + WayX *wayx The extended way. + + index_t index The index of this way in the total. + ++++++++++++++++++++++++++++++++++++++*/ + +static int index_by_id(WayX *wayx,index_t index) +{ + sortwaysx->idata[index]=wayx->id; + + return(1); +} + + +/*++++++++++++++++++++++++++++++++++++++ + Find a particular way index. + + index_t IndexWayX Returns the index of the extended way with the specified id. + + WaysX* waysx The set of ways to process. + + way_t id The way id to look for. + ++++++++++++++++++++++++++++++++++++++*/ + +index_t IndexWayX(WaysX* waysx,way_t id) +{ + int start=0; + int end=waysx->number-1; + int mid; + + assert(waysx->idata); /* Must have idata filled in => sorted */ + + /* Binary search - search key exact match only is required. + * + * # <- start | Check mid and move start or end if it doesn't match + * # | + * # | Since an exact match is wanted we can set end=mid-1 + * # <- mid | or start=mid+1 because we know that mid doesn't match. + * # | + * # | Eventually either end=start or end=start+1 and one of + * # <- end | start or end is the wanted one. + */ + + if(endidata[start]) /* Check key is not before start */ + return(NO_WAY); + else if(id>waysx->idata[end]) /* Check key is not after end */ + return(NO_WAY); + else + { + do + { + mid=(start+end)/2; /* Choose mid point */ + + if(waysx->idata[mid]idata[mid]>id) /* Mid point is too high */ + end=mid-1; + else /* Mid point is correct */ + return(mid); + } + while((end-start)>1); + + if(waysx->idata[start]==id) /* Start is correct */ + return(start); + + if(waysx->idata[end]==id) /* End is correct */ + return(end); + } + + return(NO_WAY); +} + + +/*++++++++++++++++++++++++++++++++++++++ + Lookup a particular way. + + WayX *LookupWayX Returns a pointer to the extended way with the specified id. + + WaysX* waysx The set of ways to process. + + index_t index The way index to look for. + + int position The position in the cache to use. + ++++++++++++++++++++++++++++++++++++++*/ + +WayX *LookupWayX(WaysX* waysx,index_t index,int position) +{ + assert(index!=NO_WAY); /* Must be a valid way */ + + if(option_slim) + { + SeekFile(waysx->fd,index*sizeof(WayX)); + + ReadFile(waysx->fd,&waysx->cached[position-1],sizeof(WayX)); + + return(&waysx->cached[position-1]); + } + else + { + return(&waysx->xdata[index]); + } +} + + +/*++++++++++++++++++++++++++++++++++++++ + Save the way list to a file. + + WaysX* waysx The set of ways to save. + + const char *filename The name of the file to save. + ++++++++++++++++++++++++++++++++++++++*/ + +void SaveWayList(WaysX* waysx,const char *filename) +{ + index_t i; + int fd,nfd; + int position=0; + Ways *ways; + + printf("Writing Ways: Ways=0"); + fflush(stdout); + + if(!option_slim) + waysx->xdata=MapFile(waysx->filename); + + /* Fill in a Ways structure with the offset of the real data in the file after + the Way structure itself. */ + + ways=calloc(1,sizeof(Ways)); + + assert(ways); /* Check calloc() worked */ + + ways->number=waysx->cnumber; + ways->onumber=waysx->number; + + ways->allow=0; + ways->props=0; + + ways->data=NULL; + ways->ways=NULL; + ways->names=NULL; + + /* Write out the Ways structure and then the real data. */ + + fd=OpenFile(filename); + + for(i=0;inumber;i++) + { + WayX *wayx=LookupWayX(waysx,i,1); + + ways->allow|=wayx->way.allow; + ways->props|=wayx->way.props; + + SeekFile(fd,sizeof(Ways)+wayx->prop*sizeof(Way)); + WriteFile(fd,&wayx->way,sizeof(Way)); + + if(!((i+1)%10000)) + { + printf("\rWriting Ways: Ways=%d",i+1); + fflush(stdout); + } + } + + SeekFile(fd,0); + WriteFile(fd,ways,sizeof(Ways)); + + if(!option_slim) + waysx->xdata=UnmapFile(waysx->filename); + + SeekFile(fd,sizeof(Ways)+ways->number*sizeof(Way)); + + nfd=ReOpenFile(waysx->nfilename); + + while(positionnlength) + { + int len=1024; + char temp[1024]; + + if((waysx->nlength-position)<1024) + len=waysx->nlength-position; + + ReadFile(nfd,temp,len); + WriteFile(fd,temp,len); + + position+=len; + } + + CloseFile(nfd); + + CloseFile(fd); + + printf("\rWrote Ways: Ways=%d \n",waysx->number); + fflush(stdout); + + /* Free the fake Ways */ + + free(ways); +} diff --git a/src/waysx.h b/src/waysx.h new file mode 100644 index 0000000..1aa8b62 --- /dev/null +++ b/src/waysx.h @@ -0,0 +1,87 @@ +/*************************************** + $Header: /home/amb/routino/src/RCS/waysx.h,v 1.21 2010/05/22 18:40:47 amb Exp $ + + A header file for the extended Ways structure. + + Part of the Routino routing software. + ******************/ /****************** + This file Copyright 2008-2010 Andrew M. Bishop + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + ***************************************/ + + +#ifndef WAYSX_H +#define WAYSX_H /*+ To stop multiple inclusions. +*/ + +#include + +#include "typesx.h" +#include "types.h" +#include "ways.h" + + +/* Data structures */ + + +/*+ An extended structure containing a single way. +*/ +struct _WayX +{ + way_t id; /*+ The way identifier. +*/ + + index_t prop; /*+ The index of the properties of the way in the compacted list. +*/ + + Way way; /*+ The real Way data. +*/ +}; + + +/*+ A structure containing a set of ways (memory format). +*/ +struct _WaysX +{ + char *filename; /*+ The name of the temporary file (for the WaysX). +*/ + int fd; /*+ The file descriptor of the temporary file (for the WaysX). +*/ + + uint32_t xnumber; /*+ The number of unsorted extended ways. +*/ + + WayX *xdata; /*+ The extended data for the Ways (sorted). +*/ + WayX cached[2]; /*+ Two cached ways read from the file in slim mode. +*/ + + uint32_t number; /*+ How many entries are still useful? +*/ + + uint32_t cnumber; /*+ How many entries are there after compacting? +*/ + + index_t *idata; /*+ The index of the extended data for the Ways (sorted by ID). +*/ + + char *nfilename; /*+ The name of the temporary file (for the names). +*/ + + uint32_t nlength; /*+ How long is the string of name entries? +*/ +}; + + +/* Functions */ + + +WaysX *NewWayList(int append); +void FreeWayList(WaysX *waysx,int keep); + +void SaveWayList(WaysX *waysx,const char *filename); + +index_t IndexWayX(WaysX* waysx,way_t id); +WayX *LookupWayX(WaysX* waysx,index_t index,int position); + +void AppendWay(WaysX* waysx,way_t id,Way *way,const char *name); + +void SortWayList(WaysX *waysx); + +#endif /* WAYSX_H */ diff --git a/src/xml/Makefile b/src/xml/Makefile new file mode 100644 index 0000000..81bfd04 --- /dev/null +++ b/src/xml/Makefile @@ -0,0 +1,134 @@ +# $Header: /home/amb/routino/src/xml/RCS/Makefile,v 1.10 2010/07/07 17:26:24 amb Exp $ +# +# XML test programs Makefile +# +# Part of the Routino routing software. +# +# This file Copyright 2010 Andrew M. Bishop +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# + +# Programs + +CC=gcc +LD=gcc + +# Program options + +CFLAGS=-Wall -Wmissing-prototypes -O0 -g +LDFLAGS=-lm -lc + +# Required to use stdio with files > 2GiB on 32-bit system. + +FLAGS64=-D_FILE_OFFSET_BITS=64 + +# Compilation targets + +XMLDIR=../../xml + +X=$(notdir $(wildcard $(XMLDIR)/*.xsd)) +C=$(foreach f,$(X),$(addsuffix -skeleton.c,$(basename $f))) +D=$(foreach f,$(C),$(addprefix .deps/,$(addsuffix .d,$(basename $f)))) +O=$(foreach f,$(C),$(addsuffix .o,$(basename $f))) +E=$(foreach f,$(C),$(basename $f)) + +EXE=xsd-to-xmlparser + +######## + +all : $(EXE) $(C) $(E) + @true + +######## + +xsd-to-xmlparser : xsd-to-xmlparser.o ../xmlparse.o + $(LD) xsd-to-xmlparser.o ../xmlparse.o -o $@ $(LDFLAGS) + +######## + +%-skeleton.c : $(XMLDIR)/%.xsd xsd-to-xmlparser + -./xsd-to-xmlparser < $< > $@ + @test -s $@ || rm $@ + +%-skeleton : %-skeleton.o ../xmlparse.o + $(LD) $< ../xmlparse.o -o $@ $(LDFLAGS) + +.SECONDARY : $(O) + +######## + +../xmlparse.o : ../xmlparse.c ../xmlparse.h + cd .. && $(MAKE) xmlparse.o + +../xmlparse.c : ../xmlparse.l + cd .. && $(MAKE) xmlparse.o + +######## + +%.o : %.c + $(CC) -c $(CFLAGS) $(FLAGS64) -I.. $< -o $@ -MMD -MP -MF $(addprefix .deps/,$(addsuffix .d,$(basename $<))) + +######## + +test : all test-skeleton .FORCE + @status=true ;\ + echo "" ;\ + for good in test/good*.xml; do \ + echo "Testing: $$good ... " ;\ + if ./test-skeleton < $$good > /dev/null; then echo "... passed"; else echo "... FAILED"; status=false; fi ;\ + echo "" ;\ + done ;\ + for bad in test/bad*.xml; do \ + echo "Testing: $$bad ... " ;\ + if ./test-skeleton < $$bad > /dev/null; then echo "... FAILED"; status=false; else echo "... passed"; fi ;\ + echo "" ;\ + done ;\ + if $$status; then echo "Success: all tests passed"; else echo "Warning: Some tests FAILED"; fi ;\ + $$status + +test-skeleton : test-skeleton.o + $(LD) $< ../xmlparse.o -o $@ $(LDFLAGS) + +test-skeleton.c : test/test.xsd xsd-to-xmlparser + ./xsd-to-xmlparser < $< | sed -e 's/XMLPARSE_UNKNOWN_ATTR_WARN/XMLPARSE_UNKNOWN_ATTR_ERROR/' > $@ + +######## + +clean: + rm -f *.o + rm -f *~ + +######## + +distclean: clean + -rm -f $(EXE) + -rm -f $(E) test-skeleton + -rm -f $(D) .deps/test-skeleton.d + -rm -f $(C) test-skeleton.c + -rm -fr .deps + +######## + +.deps : .FORCE + @[ -d .deps ] || mkdir $@ + +$(D) : .deps + @touch $@ + +include $(D) + +######## + +.FORCE : diff --git a/src/xml/test/bad-attr-character-ref.xml b/src/xml/test/bad-attr-character-ref.xml new file mode 100644 index 0000000..6ab951c --- /dev/null +++ b/src/xml/test/bad-attr-character-ref.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/src/xml/test/bad-attr-entity-ref.xml b/src/xml/test/bad-attr-entity-ref.xml new file mode 100644 index 0000000..7a3a5ba --- /dev/null +++ b/src/xml/test/bad-attr-entity-ref.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/src/xml/test/bad-cdata-start.xml b/src/xml/test/bad-cdata-start.xml new file mode 100644 index 0000000..cd9c4ed --- /dev/null +++ b/src/xml/test/bad-cdata-start.xml @@ -0,0 +1,13 @@ + + + + + + + + + ]]> + + + + diff --git a/src/xml/test/bad-comment-ends-triple-dash.xml b/src/xml/test/bad-comment-ends-triple-dash.xml new file mode 100644 index 0000000..17a105a --- /dev/null +++ b/src/xml/test/bad-comment-ends-triple-dash.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/src/xml/test/bad-double-quote-attr-amp.xml b/src/xml/test/bad-double-quote-attr-amp.xml new file mode 100644 index 0000000..fd58f46 --- /dev/null +++ b/src/xml/test/bad-double-quote-attr-amp.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/src/xml/test/bad-double-quote-attr-left-angle.xml b/src/xml/test/bad-double-quote-attr-left-angle.xml new file mode 100644 index 0000000..c6dc80b --- /dev/null +++ b/src/xml/test/bad-double-quote-attr-left-angle.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/src/xml/test/bad-double-quote-attr-right-angle.xml b/src/xml/test/bad-double-quote-attr-right-angle.xml new file mode 100644 index 0000000..f4c0428 --- /dev/null +++ b/src/xml/test/bad-double-quote-attr-right-angle.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/src/xml/test/bad-early-end-of-file.xml b/src/xml/test/bad-early-end-of-file.xml new file mode 100644 index 0000000..7990b44 --- /dev/null +++ b/src/xml/test/bad-early-end-of-file.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/src/xml/test/bad-end-tag-space-at-begin1.xml b/src/xml/test/bad-end-tag-space-at-begin1.xml new file mode 100644 index 0000000..ca9f42a --- /dev/null +++ b/src/xml/test/bad-end-tag-space-at-begin1.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/src/xml/test/bad-end-tag-space-at-begin2.xml b/src/xml/test/bad-end-tag-space-at-begin2.xml new file mode 100644 index 0000000..aeae822 --- /dev/null +++ b/src/xml/test/bad-end-tag-space-at-begin2.xml @@ -0,0 +1,12 @@ + + + + + + + + + + < /level1> + + diff --git a/src/xml/test/bad-end-tag-space-at-end.xml b/src/xml/test/bad-end-tag-space-at-end.xml new file mode 100644 index 0000000..b060674 --- /dev/null +++ b/src/xml/test/bad-end-tag-space-at-end.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/src/xml/test/bad-end-tag-with-attr.xml b/src/xml/test/bad-end-tag-with-attr.xml new file mode 100644 index 0000000..dab05b1 --- /dev/null +++ b/src/xml/test/bad-end-tag-with-attr.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/src/xml/test/bad-single-quote-attr-amp.xml b/src/xml/test/bad-single-quote-attr-amp.xml new file mode 100644 index 0000000..30200c5 --- /dev/null +++ b/src/xml/test/bad-single-quote-attr-amp.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/src/xml/test/bad-single-quote-attr-left-angle.xml b/src/xml/test/bad-single-quote-attr-left-angle.xml new file mode 100644 index 0000000..10c8ba0 --- /dev/null +++ b/src/xml/test/bad-single-quote-attr-left-angle.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/src/xml/test/bad-single-quote-attr-right-angle.xml b/src/xml/test/bad-single-quote-attr-right-angle.xml new file mode 100644 index 0000000..d043b8a --- /dev/null +++ b/src/xml/test/bad-single-quote-attr-right-angle.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/src/xml/test/bad-start-tag-space-at-begin.xml b/src/xml/test/bad-start-tag-space-at-begin.xml new file mode 100644 index 0000000..02e81bb --- /dev/null +++ b/src/xml/test/bad-start-tag-space-at-begin.xml @@ -0,0 +1,12 @@ + + + + + + + < level1> + + + + + diff --git a/src/xml/test/bad-tag-attr-no-quotes.xml b/src/xml/test/bad-tag-attr-no-quotes.xml new file mode 100644 index 0000000..bdc476e --- /dev/null +++ b/src/xml/test/bad-tag-attr-no-quotes.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/src/xml/test/bad-tag-attr-space-after-equal.xml b/src/xml/test/bad-tag-attr-space-after-equal.xml new file mode 100644 index 0000000..1ba6847 --- /dev/null +++ b/src/xml/test/bad-tag-attr-space-after-equal.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/src/xml/test/bad-tag-attr-space-before-equal.xml b/src/xml/test/bad-tag-attr-space-before-equal.xml new file mode 100644 index 0000000..06236c6 --- /dev/null +++ b/src/xml/test/bad-tag-attr-space-before-equal.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/src/xml/test/bad-tag-level-nesting.xml b/src/xml/test/bad-tag-level-nesting.xml new file mode 100644 index 0000000..dba5592 --- /dev/null +++ b/src/xml/test/bad-tag-level-nesting.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/src/xml/test/bad-unbalanced-tag-start-end.xml b/src/xml/test/bad-unbalanced-tag-start-end.xml new file mode 100644 index 0000000..e375475 --- /dev/null +++ b/src/xml/test/bad-unbalanced-tag-start-end.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/src/xml/test/bad-unexpected-attribute-name.xml b/src/xml/test/bad-unexpected-attribute-name.xml new file mode 100644 index 0000000..9b17fdd --- /dev/null +++ b/src/xml/test/bad-unexpected-attribute-name.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/src/xml/test/bad-unexpected-end-tag.xml b/src/xml/test/bad-unexpected-end-tag.xml new file mode 100644 index 0000000..219d79b --- /dev/null +++ b/src/xml/test/bad-unexpected-end-tag.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/src/xml/test/bad-unexpected-left-angle.xml b/src/xml/test/bad-unexpected-left-angle.xml new file mode 100644 index 0000000..d8ecbea --- /dev/null +++ b/src/xml/test/bad-unexpected-left-angle.xml @@ -0,0 +1,13 @@ + + + + + + + + + < + + + + diff --git a/src/xml/test/bad-unexpected-right-angle.xml b/src/xml/test/bad-unexpected-right-angle.xml new file mode 100644 index 0000000..358524e --- /dev/null +++ b/src/xml/test/bad-unexpected-right-angle.xml @@ -0,0 +1,13 @@ + + + + + + + + + > + + + + diff --git a/src/xml/test/bad-xml-header-at-begin.xml b/src/xml/test/bad-xml-header-at-begin.xml new file mode 100644 index 0000000..e19fc5f --- /dev/null +++ b/src/xml/test/bad-xml-header-at-begin.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/src/xml/test/bad-xml-header-at-end.xml b/src/xml/test/bad-xml-header-at-end.xml new file mode 100644 index 0000000..2a3857d --- /dev/null +++ b/src/xml/test/bad-xml-header-at-end.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/src/xml/test/bad-xml-header-not-first.xml b/src/xml/test/bad-xml-header-not-first.xml new file mode 100644 index 0000000..cdabc6b --- /dev/null +++ b/src/xml/test/bad-xml-header-not-first.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/src/xml/test/good.xml b/src/xml/test/good.xml new file mode 100644 index 0000000..f7e207c --- /dev/null +++ b/src/xml/test/good.xml @@ -0,0 +1,13 @@ + + + + + + + + + ]]> + + + + diff --git a/src/xml/test/test.xsd b/src/xml/test/test.xsd new file mode 100644 index 0000000..fce04ee --- /dev/null +++ b/src/xml/test/test.xsd @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/xml/xsd-to-xmlparser.c b/src/xml/xsd-to-xmlparser.c new file mode 100644 index 0000000..6e15d9d --- /dev/null +++ b/src/xml/xsd-to-xmlparser.c @@ -0,0 +1,514 @@ +/*************************************** + $Header: /home/amb/routino/src/xml/RCS/xsd-to-xmlparser.c,v 1.10 2010/04/23 18:41:20 amb Exp $ + + An XML parser for simplified XML Schema Definitions to create XML parser skeletons. + + Part of the Routino routing software. + ******************/ /****************** + This file Copyright 2010 Andrew M. Bishop + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + ***************************************/ + + +#include +#include +#include +#include + +#include "xmlparse.h" + + +/*+ A forward definition of the xmltagx +*/ +typedef struct _xmltagx xmltagx; + + +/*+ A structure to hold the extended definition of a tag. +*/ +struct _xmltagx +{ + char *name; /*+ The name of the tag. +*/ + char *type; /*+ The type of the tag. +*/ + + int nattributes; /*+ The number of valid attributes for the tag. +*/ + char *attributes[XMLPARSE_MAX_ATTRS]; /*+ The valid attributes for the tag. +*/ + + int nsubtagsx; /*+ The number of valid attributes for the tag. +*/ + xmltagx *subtagsx[XMLPARSE_MAX_SUBTAGS]; /*+ The list of types for the subtags contained within this one. +*/ +}; + + +/* The local variables and functions */ + +int ntagsx=0; +xmltagx **tagsx=NULL; +char *currenttype=NULL; + +static char *safe(const char *name); + + +/* The XML tag processing function prototypes */ + +static int xmlDeclaration_function(const char *_tag_,int _type_,const char *version,const char *encoding); +static int schemaType_function(const char *_tag_,int _type_,const char *elementFormDefault,const char *xmlns_xsd); +static int complexType_function(const char *_tag_,int _type_,const char *name); +static int attributeType_function(const char *_tag_,int _type_,const char *name,const char *type); +static int sequenceType_function(const char *_tag_,int _type_); +static int elementType_function(const char *_tag_,int _type_,const char *name,const char *type,const char *minOccurs,const char *maxOccurs); + + +/* The XML tag definitions */ + +/*+ The elementType type tag. +*/ +static xmltag elementType_tag= + {"xsd:element", + 4, {"name","type","minOccurs","maxOccurs"}, + elementType_function, + {NULL}}; + +/*+ The sequenceType type tag. +*/ +static xmltag sequenceType_tag= + {"xsd:sequence", + 0, {NULL}, + sequenceType_function, + {&elementType_tag,NULL}}; + +/*+ The attributeType type tag. +*/ +static xmltag attributeType_tag= + {"xsd:attribute", + 2, {"name","type"}, + attributeType_function, + {NULL}}; + +/*+ The complexType type tag. +*/ +static xmltag complexType_tag= + {"xsd:complexType", + 1, {"name"}, + complexType_function, + {&sequenceType_tag,&attributeType_tag,NULL}}; + +/*+ The schemaType type tag. +*/ +static xmltag schemaType_tag= + {"xsd:schema", + 2, {"elementFormDefault","xmlns:xsd"}, + schemaType_function, + {&elementType_tag,&complexType_tag,NULL}}; + +/*+ The xmlDeclaration type tag. +*/ +static xmltag xmlDeclaration_tag= + {"xml", + 2, {"version","encoding"}, + xmlDeclaration_function, + {NULL}}; + + +/*+ The complete set of tags at the top level. +*/ +static xmltag *xml_toplevel_tags[]={&xmlDeclaration_tag,&schemaType_tag,NULL}; + + +/* The XML tag processing functions */ + + +/*++++++++++++++++++++++++++++++++++++++ + The function that is called when the elementType XSD type is seen + + int elementType_function Returns 0 if no error occured or something else otherwise. + + const char *_tag_ Set to the name of the element tag that triggered this function call. + + int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag. + + const char *name The contents of the 'name' attribute (or NULL if not defined). + + const char *type The contents of the 'type' attribute (or NULL if not defined). + + const char *minOccurs The contents of the 'minOccurs' attribute (or NULL if not defined). + + const char *maxOccurs The contents of the 'maxOccurs' attribute (or NULL if not defined). + ++++++++++++++++++++++++++++++++++++++*/ + +static int elementType_function(const char *_tag_,int _type_,const char *name,const char *type,const char *minOccurs,const char *maxOccurs) +{ + xmltagx *tagx=NULL; + int i; + + if(_type_==XMLPARSE_TAG_END) + return(0); + + for(i=0;itype) && !strcmp(name,tagsx[i]->name)) + tagx=tagsx[i]; + + if(!tagx) + { + ntagsx++; + tagsx=(xmltagx**)realloc((void*)tagsx,ntagsx*sizeof(xmltagx*)); + + tagsx[ntagsx-1]=(xmltagx*)calloc(1,sizeof(xmltagx)); + tagsx[ntagsx-1]->name=strcpy(malloc(strlen(name)+1),name); + tagsx[ntagsx-1]->type=strcpy(malloc(strlen(type)+1),type); + + tagx=tagsx[ntagsx-1]; + } + + if(!currenttype) + return(0); + + for(i=0;itype,currenttype)) + { + tagsx[i]->subtagsx[tagsx[i]->nsubtagsx]=tagx; + tagsx[i]->nsubtagsx++; + + if(tagsx[i]->nsubtagsx==XMLPARSE_MAX_SUBTAGS) + {fprintf(stderr,"Too many subtags seen for type '%s'.\n",currenttype); exit(1);} + } + + return(0); +} + + +/*++++++++++++++++++++++++++++++++++++++ + The function that is called when the sequenceType XSD type is seen + + int sequenceType_function Returns 0 if no error occured or something else otherwise. + + const char *_tag_ Set to the name of the element tag that triggered this function call. + + int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag. + ++++++++++++++++++++++++++++++++++++++*/ + +static int sequenceType_function(const char *_tag_,int _type_) +{ + return(0); +} + + +/*++++++++++++++++++++++++++++++++++++++ + The function that is called when the attributeType XSD type is seen + + int attributeType_function Returns 0 if no error occured or something else otherwise. + + const char *_tag_ Set to the name of the element tag that triggered this function call. + + int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag. + + const char *name The contents of the 'name' attribute (or NULL if not defined). + + const char *type The contents of the 'type' attribute (or NULL if not defined). + ++++++++++++++++++++++++++++++++++++++*/ + +static int attributeType_function(const char *_tag_,int _type_,const char *name,const char *type) +{ + int i; + + if(_type_==XMLPARSE_TAG_END) + return(0); + + for(i=0;itype,currenttype)) + { + tagsx[i]->attributes[tagsx[i]->nattributes]=strcpy(malloc(strlen(name)+1),name); + tagsx[i]->nattributes++; + + if(tagsx[i]->nattributes==XMLPARSE_MAX_ATTRS) + {fprintf(stderr,"Too many attributes seen for type '%s'.\n",currenttype); exit(1);} + } + + return(0); +} + + +/*++++++++++++++++++++++++++++++++++++++ + The function that is called when the complexType XSD type is seen + + int complexType_function Returns 0 if no error occured or something else otherwise. + + const char *_tag_ Set to the name of the element tag that triggered this function call. + + int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag. + + const char *name The contents of the 'name' attribute (or NULL if not defined). + ++++++++++++++++++++++++++++++++++++++*/ + +static int complexType_function(const char *_tag_,int _type_,const char *name) +{ + if(_type_==XMLPARSE_TAG_END) + return(0); + + currenttype=strcpy(realloc(currenttype,strlen(name)+1),name); + + return(0); +} + + +/*++++++++++++++++++++++++++++++++++++++ + The function that is called when the schemaType XSD type is seen + + int schemaType_function Returns 0 if no error occured or something else otherwise. + + const char *_tag_ Set to the name of the element tag that triggered this function call. + + int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag. + + const char *elementFormDefault The contents of the 'elementFormDefault' attribute (or NULL if not defined). + + const char *xmlns_xsd The contents of the 'xmlns:xsd' attribute (or NULL if not defined). + ++++++++++++++++++++++++++++++++++++++*/ + +static int schemaType_function(const char *_tag_,int _type_,const char *elementFormDefault,const char *xmlns_xsd) +{ + return(0); +} + + +/*++++++++++++++++++++++++++++++++++++++ + The function that is called when the XML declaration is seen + + int xmlDeclaration_function Returns 0 if no error occured or something else otherwise. + + const char *_tag_ Set to the name of the element tag that triggered this function call. + + int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag. + + const char *version The contents of the 'version' attribute (or NULL if not defined). + + const char *encoding The contents of the 'encoding' attribute (or NULL if not defined). + ++++++++++++++++++++++++++++++++++++++*/ + +static int xmlDeclaration_function(const char *_tag_,int _type_,const char *version,const char *encoding) +{ + return(0); +} + + +/*++++++++++++++++++++++++++++++++++++++ + The XML Schema Definition XML parser and C program generator. + ++++++++++++++++++++++++++++++++++++++*/ + +int main(int argc,char **argv) +{ + int i,j,k; + + if(ParseXML(stdin,xml_toplevel_tags,XMLPARSE_UNKNOWN_ATTR_IGNORE)) + { + fprintf(stderr,"Cannot parse XML file - exiting.\n"); + exit(1); + } + + /* Add the XML declaration as a tag. */ + + currenttype=NULL; + elementType_function("xsd:element",XMLPARSE_TAG_START|XMLPARSE_TAG_END,"xml","xmlDeclaration",NULL,NULL); + complexType_function("xsd:complexType",XMLPARSE_TAG_START,"xmlDeclaration"); + attributeType_function("xsd:attribute",XMLPARSE_TAG_START|XMLPARSE_TAG_END,"version",NULL); + attributeType_function("xsd:attribute",XMLPARSE_TAG_START|XMLPARSE_TAG_END,"encoding",NULL); + complexType_function("xsd:complexType",XMLPARSE_TAG_END,NULL); + + /* Sort the tags */ + + sorttags: + + for(i=0;insubtagsx;j++) + { + for(k=0;ksubtagsx[j]==tagsx[k]) + break; + + if(i\n"); + printf("\n"); + printf("#include \"xmlparse.h\"\n"); + + /* Print the function prototypes */ + + printf("\n"); + printf("\n"); + printf("/* The XML tag processing function prototypes */\n"); + printf("\n"); + + for(i=ntagsx-1;i>=0;i--) + { + printf("static int %s_function(const char *_tag_,int _type_",safe(tagsx[i]->type)); + + for(j=0;jnattributes;j++) + printf(",const char *%s",safe(tagsx[i]->attributes[j])); + + printf(");\n"); + } + + /* Print the xmltag variables */ + + printf("\n"); + printf("\n"); + printf("/* The XML tag definitions */\n"); + + for(i=0;itype); + printf("static xmltag %s_tag=\n",safe(tagsx[i]->type)); + printf(" {\"%s\",\n",tagsx[i]->name); + + printf(" %d, {",tagsx[i]->nattributes); + for(j=0;jnattributes;j++) + printf("%s\"%s\"",(j?",":""),tagsx[i]->attributes[j]); + printf("%s},\n",(tagsx[i]->nattributes?"":"NULL")); + + printf(" %s_function,\n",safe(tagsx[i]->type)); + + printf(" {"); + for(j=0;jnsubtagsx;j++) + printf("&%s_tag,",safe(tagsx[i]->subtagsx[j]->type)); + printf("NULL}};\n"); + } + + printf("\n"); + printf("\n"); + printf("/*+ The complete set of tags at the top level. +*/\n"); + printf("static xmltag *xml_toplevel_tags[]={"); + printf("&%s_tag,",safe(tagsx[ntagsx-1]->type)); + printf("&%s_tag,",safe(tagsx[ntagsx-2]->type)); + printf("NULL};\n"); + + /* Print the functions */ + + printf("\n"); + printf("\n"); + printf("/* The XML tag processing functions */\n"); + + for(i=0;itype); + printf("\n"); + printf(" int %s_function Returns 0 if no error occured or something else otherwise.\n",safe(tagsx[i]->type)); + printf("\n"); + printf(" const char *_tag_ Set to the name of the element tag that triggered this function call.\n"); + printf("\n"); + printf(" int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag.\n"); + for(j=0;jnattributes;j++) + { + printf("\n"); + printf(" const char *%s The contents of the '%s' attribute (or NULL if not defined).\n",safe(tagsx[i]->attributes[j]),tagsx[i]->attributes[j]); + } + printf(" ++++++++++++++++++++++++++++++++++++++*/\n"); + printf("\n"); + + printf("static int %s_function(const char *_tag_,int _type_",safe(tagsx[i]->type)); + + for(j=0;jnattributes;j++) + printf(",const char *%s",safe(tagsx[i]->attributes[j])); + + printf(")\n"); + + printf("{\n"); + + if(i==(ntagsx-1)) /* XML tag */ + { + printf(" printf(\"nattributes;j++) + { + char *safename=safe(tagsx[i]->attributes[j]); + printf(" if(%s) printf(\" %s=\\\"%%s\\\"\",ParseXML_Encode_Safe_XML(%s));\n",safename,tagsx[i]->attributes[j],safename); + } + printf(" printf(\" ?>\\n\");\n"); + } + else + { + printf(" printf(\"<%%s%%s\",(_type_==XMLPARSE_TAG_END)?\"/\":\"\",_tag_);\n"); + for(j=0;jnattributes;j++) + { + char *safename=safe(tagsx[i]->attributes[j]); + printf(" if(%s) printf(\" %s=\\\"%%s\\\"\",ParseXML_Encode_Safe_XML(%s));\n",safename,tagsx[i]->attributes[j],safename); + } + printf(" printf(\"%%s>\\n\",(_type_==(XMLPARSE_TAG_START|XMLPARSE_TAG_END))?\" /\":\"\");\n"); + } + + printf(" return(0);\n"); + printf("}\n"); + } + + /* Print the main function */ + + printf("\n"); + printf("\n"); + printf("/*++++++++++++++++++++++++++++++++++++++\n"); + printf(" A skeleton XML parser.\n"); + printf(" ++++++++++++++++++++++++++++++++++++++*/\n"); + printf("\n"); + printf("int main(int argc,char **argv)\n"); + printf("{\n"); + printf(" if(ParseXML(stdin,xml_toplevel_tags,XMLPARSE_UNKNOWN_ATTR_WARN))\n"); + printf(" return(1);\n"); + printf(" else\n"); + printf(" return(0);\n"); + printf("}\n"); + + return(0); +} + + +/*++++++++++++++++++++++++++++++++++++++ + A function to return a safe C identifier from an XML tag or attribute name. + + char *safe Returns the safe name in a private string (only use once). + + const char *name The name to convert. + ++++++++++++++++++++++++++++++++++++++*/ + +static char *safe(const char *name) +{ + static char *safe=NULL; + int i; + + safe=realloc(safe,strlen(name)+1); + + for(i=0;name[i];i++) + if(isalnum(name[i])) + safe[i]=name[i]; + else + safe[i]='_'; + + safe[i]=0; + + return(safe); +} diff --git a/src/xmlparse.h b/src/xmlparse.h new file mode 100644 index 0000000..93ff0c9 --- /dev/null +++ b/src/xmlparse.h @@ -0,0 +1,136 @@ +/*************************************** + $Header: /home/amb/routino/src/RCS/xmlparse.h,v 1.12 2010/05/14 17:55:56 amb Exp $ + + A simple XML parser + + Part of the Routino routing software. + ******************/ /****************** + This file Copyright 2010 Andrew M. Bishop + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + ***************************************/ + + +#ifndef XMLPARSE_H +#define XMLPARSE_H /*+ To stop multiple inclusions. +*/ + +#include + + +/*+ The maximum number of attributes per tag. +*/ +#define XMLPARSE_MAX_ATTRS 16 + +/*+ The maximum number of subtags per tag. +*/ +#define XMLPARSE_MAX_SUBTAGS 16 + +/*+ A flag to indicate the start and/or end of a tag. +*/ +#define XMLPARSE_TAG_START 1 +#define XMLPARSE_TAG_END 2 + + +/*+ A forward definition of the xmltag +*/ +typedef struct _xmltag xmltag; + + +/*+ A structure to hold the definition of a tag. +*/ +struct _xmltag +{ + char *name; /*+ The name of the tag. +*/ + + int nattributes; /*+ The number of valid attributes for the tag. +*/ + char *attributes[XMLPARSE_MAX_ATTRS]; /*+ The valid attributes for the tag. +*/ + + int (*callback)(); /*+ The callback function when the tag is seen. +*/ + + xmltag *subtags[XMLPARSE_MAX_SUBTAGS]; /*+ The list of valid tags contained within this one (null terminated). +*/ +}; + + +/* XML Parser options */ + +#define XMLPARSE_UNKNOWN_ATTRIBUTES 0x0003 +#define XMLPARSE_UNKNOWN_ATTR_ERROR 0x0000 /* Flag an error and exit. */ +#define XMLPARSE_UNKNOWN_ATTR_ERRNONAME 0x0001 /* Flag an error and exit unless a namespace is specified. */ +#define XMLPARSE_UNKNOWN_ATTR_WARN 0x0002 /* Warn about the problem and continue. */ +#define XMLPARSE_UNKNOWN_ATTR_IGNORE 0x0003 /* Ignore the potential problem. */ + +#define XMLPARSE_RETURN_ATTR_ENCODED 0x0004 /* Return the XML attribute strings without decoding them. */ + + +/* XML parser functions */ + +int ParseXML(FILE *file,xmltag **tags,int options); + +unsigned long ParseXML_LineNumber(void); + +char *ParseXML_Decode_Entity_Ref(const char *string); +char *ParseXML_Decode_Char_Ref(const char *string); +char *ParseXML_Encode_Safe_XML(const char *string); + +int ParseXML_GetInteger(const char *string,int *number); +int ParseXML_GetFloating(const char *string,double *number); + +/* Macros to simplify the callback functions */ + +#define XMLPARSE_MESSAGE(tag,message) \ + do \ + { \ + fprintf(stderr,"XML Parser: Error on line %ld: " message " in <%s> tag.\n",ParseXML_LineNumber(),tag); \ + return(1); \ + } \ + while(0) + +#define XMLPARSE_INVALID(tag,attribute) \ + do \ + { \ + fprintf(stderr,"XML Parser: Error on line %ld: Invalid value for '" #attribute "' attribute in <%s> tag.\n",ParseXML_LineNumber(),tag); \ + return(1); \ + } \ + while(0) + +#define XMLPARSE_ASSERT_STRING(tag,attribute) \ + do \ + { \ + if(!attribute) \ + { \ + fprintf(stderr,"XML Parser: Error on line %ld: '" #attribute "' attribute must be specified in <%s> tag.\n",ParseXML_LineNumber(),tag); \ + return(1); \ + } \ + } \ + while(0) + +#define XMLPARSE_ASSERT_INTEGER(tag,attribute,result) \ + do \ + { \ + if(!attribute || !*attribute || !ParseXML_GetInteger(attribute,&result)) \ + { \ + fprintf(stderr,"XML Parser: Error on line %ld: '" #attribute "' attribute must be a integer in <%s> tag.\n",ParseXML_LineNumber(),tag); \ + return(1); \ + } \ + } \ + while(0) + +#define XMLPARSE_ASSERT_FLOATING(tag,attribute,result) \ + do \ + { \ + if(!attribute || !*attribute || !ParseXML_GetFloating(attribute,&result)) \ + { \ + fprintf(stderr,"XML Parser: Error on line %ld: '" #attribute "' attribute must be a number in <%s> tag.\n",ParseXML_LineNumber(),tag); \ + return(1); \ + } \ + } \ + while(0) + + +#endif /* XMLPARSE_H */ diff --git a/src/xmlparse.l b/src/xmlparse.l new file mode 100644 index 0000000..c64fdf0 --- /dev/null +++ b/src/xmlparse.l @@ -0,0 +1,784 @@ +%{ +/*************************************** + $Header: /home/amb/routino/src/RCS/xmlparse.l,v 1.17 2010/05/25 18:24:20 amb Exp $ + + A simple generic XML parser where the structure comes from the function parameters. + Not intended to be fully conforming to XML staandard or a validating parser but + sufficient to parse OSM XML and simple program configuration files. + + Part of the Routino routing software. + ******************/ /****************** + This file Copyright 2010 Andrew M. Bishop + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + ***************************************/ + + +#include +#include +#include +#include + +#include "xmlparse.h" + + +/* Parser outputs */ + +#define LEX_EOF 0 + +#define LEX_TAG_BEGIN 1 +#define LEX_XML_DECL_BEGIN 2 +#define LEX_TAG_POP 3 +#define LEX_TAG_PUSH 4 +#define LEX_XML_DECL_FINISH 6 +#define LEX_TAG_FINISH 7 +#define LEX_ATTR_KEY 8 +#define LEX_ATTR_VAL 9 + +#define LEX_ERROR 100 + +#define LEX_ERROR_TAG_START 101 +#define LEX_ERROR_XML_DECL_START 102 +#define LEX_ERROR_TAG 103 +#define LEX_ERROR_XML_DECL 104 +#define LEX_ERROR_ATTR 105 +#define LEX_ERROR_END_TAG 106 +#define LEX_ERROR_COMMENT 107 +#define LEX_ERROR_CLOSE 108 +#define LEX_ERROR_ATTR_VAL 109 +#define LEX_ERROR_ENTITY_REF 110 +#define LEX_ERROR_CHAR_REF 111 + +#define LEX_ERROR_UNEXP_TAG 201 +#define LEX_ERROR_UNBALANCED 202 +#define LEX_ERROR_NO_START 203 +#define LEX_ERROR_UNEXP_ATT 204 +#define LEX_ERROR_UNEXP_EOF 205 +#define LEX_ERROR_XML_NOT_FIRST 206 + +#define LEX_ERROR_CALLBACK 255 + + +/* Lexer definitions */ + +#define YY_SKIP_YYWRAP 1 /* Remove error with prototype of ..._yywrap */ +#ifndef yywrap +/*+ Needed in lex but does nothing. +*/ +#define yywrap() 1 +#endif + +/*+ Reset the current string. +*/ +#define reset_string \ + if(!string) string=(char*)malloc(16); \ + *string=0; \ + stringused=0; + +/*+ append information to the current string. +*/ +#define append_string(xx) \ + newlen=strlen(xx); \ + if((stringused+newlen)>=stringlen) \ + string=(char*)realloc((void*)string,stringlen=(stringused+newlen+16)); \ + strcpy(string+stringused,xx); \ + stringused+=newlen; + +#define YY_NO_INPUT + + +/* Lexer functions and variables */ + +extern int yylex(void); + +static char *yylval=NULL; + +static int xmlparse_options; + +%} + +%option 8bit +%option pointer +%option batch +%option yylineno + +%option nodefault +%option perf-report +%option fast +%option nounput + + + /* Grammar based on http://www.w3.org/TR/2004/REC-xml-20040204/ but for ASCII not Unicode. */ + +S [ \t\r\n] + +letter [a-zA-Z] +digit [0-9] +xdigit [a-fA-F0-9] + +namechar ({letter}|{digit}|[-._:]) +name ({letter}|[_:]){namechar}* + +entityref &{name}; +charref &#({digit}+|x{xdigit}+); + + +%x COMMENT +%x CDATA +%x DOCTYPE +%x XML_DECL_START XML_DECL +%x TAG_START TAG +%x ATTR_KEY ATTR_VAL +%x END_TAG1 END_TAG2 +%x DQUOTED SQUOTED + +%% + /* Must use static variables since the parser returns often. */ + static char *string=NULL; + static int stringlen=0,stringused=0; + static int after_attr=0; + int newlen; + int doctype_depth=0; + + /* Handle top level entities */ + +"" { return(LEX_ERROR_COMMENT); } +"-->" { BEGIN(INITIAL); } +"--"[^->]+ { } +[^-]+ { } +"-" { } + + /* CDATA */ + +"]]>" { BEGIN(INITIAL); } +"]" { } +[^]]+ { } + + /* CDATA */ + +"<" { doctype_depth++; } +">" { if(doctype_depth==0) BEGIN(INITIAL); else doctype_depth--; } +[^<>]+ { } + + /* XML Declaration start */ + +{name} { BEGIN(XML_DECL); yylval=yytext; return(LEX_XML_DECL_BEGIN); } +.|\n { return(LEX_ERROR_XML_DECL_START); } + + /* Tag middle */ + +"?>" { BEGIN(INITIAL); return(LEX_XML_DECL_FINISH); } +{S}+ { } +{name} { after_attr=XML_DECL; BEGIN(ATTR_KEY); yylval=yytext; return(LEX_ATTR_KEY); } +.|\n { return(LEX_ERROR_XML_DECL); } + + /* Any tag start */ + +{name} { BEGIN(TAG); yylval=yytext; return(LEX_TAG_BEGIN); } +.|\n { return(LEX_ERROR_TAG_START); } + + /* End-tag start */ + +{name} { BEGIN(END_TAG2); yylval=yytext; return(LEX_TAG_POP); } +.|\n { return(LEX_ERROR_END_TAG); } + +">" { BEGIN(INITIAL); } +.|\n { return(LEX_ERROR_END_TAG); } + + /* Any tag middle */ + +"/>" { BEGIN(INITIAL); return(LEX_TAG_FINISH); } +">" { BEGIN(INITIAL); return(LEX_TAG_PUSH); } +{S}+ { } +{name} { after_attr=TAG; BEGIN(ATTR_KEY); yylval=yytext; return(LEX_ATTR_KEY); } +.|\n { return(LEX_ERROR_TAG); } + + /* Attributes */ + += { BEGIN(ATTR_VAL); } +.|\n { return(LEX_ERROR_ATTR); } + +\" { BEGIN(DQUOTED); reset_string; } +\' { BEGIN(SQUOTED); reset_string; } +.|\n { return(LEX_ERROR_ATTR); } + + /* Quoted strings */ + +\" { BEGIN(after_attr); yylval=string; return(LEX_ATTR_VAL); } +{entityref} { if(xmlparse_options&XMLPARSE_RETURN_ATTR_ENCODED) {append_string(yytext);} + else { const char *str=ParseXML_Decode_Entity_Ref(yytext); if(str) {append_string(str);} else {yylval=yytext; return(LEX_ERROR_ENTITY_REF);} } } +{charref} { if(xmlparse_options&XMLPARSE_RETURN_ATTR_ENCODED) {append_string(yytext);} + else { const char *str=ParseXML_Decode_Char_Ref(yytext); if(str) {append_string(str);} else {yylval=yytext; return(LEX_ERROR_CHAR_REF);} } } +[<>&] { yylval=yytext; return(LEX_ERROR_ATTR_VAL); } +[^<>&\"]+ { append_string(yytext); } + +\' { BEGIN(after_attr); yylval=string; return(LEX_ATTR_VAL); } +{entityref} { if(xmlparse_options&XMLPARSE_RETURN_ATTR_ENCODED) {append_string(yytext);} + else { const char *str=ParseXML_Decode_Entity_Ref(yytext); if(str) {append_string(str);} else {yylval=yytext; return(LEX_ERROR_ENTITY_REF);} } } +{charref} { if(xmlparse_options&XMLPARSE_RETURN_ATTR_ENCODED) {append_string(yytext);} + else { const char *str=ParseXML_Decode_Char_Ref(yytext); if(str) {append_string(str);} else {yylval=yytext; return(LEX_ERROR_CHAR_REF);} } } +[<>&] { yylval=yytext; return(LEX_ERROR_ATTR_VAL); } +[^<>&\']+ { append_string(yytext); } + + /* End of file */ + +<> { free(string); string=NULL; stringlen=stringused=0; BEGIN(INITIAL); return(LEX_EOF); } + +%% + + +/*++++++++++++++++++++++++++++++++++++++ + A function to call the callback function with the parameters needed. + + int call_callback Returns 1 if the callback returned with an error. + + const char *name The name of the tag. + + int (*callback)() The callback function. + + int type The type of tag (start and/or end). + + int nattributes The number of attributes collected. + + char *attributes[XMLPARSE_MAX_ATTRS] The list of attributes. + ++++++++++++++++++++++++++++++++++++++*/ + +static inline int call_callback(const char *name,int (*callback)(),int type,int nattributes,char *attributes[XMLPARSE_MAX_ATTRS]) +{ + switch(nattributes) + { + case 0: return (*callback)(name,type); + case 1: return (*callback)(name,type,attributes[0]); + case 2: return (*callback)(name,type,attributes[0],attributes[1]); + case 3: return (*callback)(name,type,attributes[0],attributes[1],attributes[2]); + case 4: return (*callback)(name,type,attributes[0],attributes[1],attributes[2],attributes[3]); + case 5: return (*callback)(name,type,attributes[0],attributes[1],attributes[2],attributes[3],attributes[4]); + case 6: return (*callback)(name,type,attributes[0],attributes[1],attributes[2],attributes[3],attributes[4],attributes[5]); + case 7: return (*callback)(name,type,attributes[0],attributes[1],attributes[2],attributes[3],attributes[4],attributes[5],attributes[6]); + case 8: return (*callback)(name,type,attributes[0],attributes[1],attributes[2],attributes[3],attributes[4],attributes[5],attributes[6],attributes[7]); + case 9: return (*callback)(name,type,attributes[0],attributes[1],attributes[2],attributes[3],attributes[4],attributes[5],attributes[6],attributes[7],attributes[8]); + case 10: return (*callback)(name,type,attributes[0],attributes[1],attributes[2],attributes[3],attributes[4],attributes[5],attributes[6],attributes[7],attributes[8],attributes[9]); + case 11: return (*callback)(name,type,attributes[0],attributes[1],attributes[2],attributes[3],attributes[4],attributes[5],attributes[6],attributes[7],attributes[8],attributes[9],attributes[10]); + case 12: return (*callback)(name,type,attributes[0],attributes[1],attributes[2],attributes[3],attributes[4],attributes[5],attributes[6],attributes[7],attributes[8],attributes[9],attributes[10],attributes[11]); + case 13: return (*callback)(name,type,attributes[0],attributes[1],attributes[2],attributes[3],attributes[4],attributes[5],attributes[6],attributes[7],attributes[8],attributes[9],attributes[10],attributes[11],attributes[12]); + case 14: return (*callback)(name,type,attributes[0],attributes[1],attributes[2],attributes[3],attributes[4],attributes[5],attributes[6],attributes[7],attributes[8],attributes[9],attributes[10],attributes[11],attributes[12],attributes[13]); + case 15: return (*callback)(name,type,attributes[0],attributes[1],attributes[2],attributes[3],attributes[4],attributes[5],attributes[6],attributes[7],attributes[8],attributes[9],attributes[10],attributes[11],attributes[12],attributes[13],attributes[14]); + case 16: return (*callback)(name,type,attributes[0],attributes[1],attributes[2],attributes[3],attributes[4],attributes[5],attributes[6],attributes[7],attributes[8],attributes[9],attributes[10],attributes[11],attributes[12],attributes[13],attributes[14],attributes[15]); + + default: + fprintf(stderr,"XML Parser: Error on line %d: too many attributes for tag '%s' source code needs changing.\n",yylineno,name); + exit(1); + } +} + + +/*++++++++++++++++++++++++++++++++++++++ + Parse the XML and call the functions for each tag as seen. + + int ParseXML Returns 0 if OK or something else in case of an error. + + FILE *file The file to parse. + + xmltag **tags The array of pointers to tags for the top level. + + int options A list of XML Parser options OR-ed together. + ++++++++++++++++++++++++++++++++++++++*/ + +int ParseXML(FILE *file,xmltag **tags,int options) +{ + int yychar,i; + + char *attributes[XMLPARSE_MAX_ATTRS]={NULL}; + int attribute=0; + + int stackdepth=0,stackused=0; + xmltag ***tags_stack=NULL; + xmltag **tag_stack=NULL; + xmltag *tag=NULL; + + /* The actual parser. */ + + xmlparse_options=options; + + yyin=file; + + yyrestart(yyin); + + yylineno=1; + + BEGIN(INITIAL); + + do + { + yychar=yylex(); + + switch(yychar) + { + /* The start of a tag for an XML declaration */ + + case LEX_XML_DECL_BEGIN: + + if(tag_stack) + { + fprintf(stderr,"XML Parser: Error on line %d: XML declaration not before all other tags.\n",yylineno); + yychar=LEX_ERROR_XML_NOT_FIRST; + break; + } + + /* The start of a tag for an element */ + + case LEX_TAG_BEGIN: + + tag=NULL; + + for(i=0;tags[i];i++) + if(!strcasecmp(yylval,tags[i]->name)) + { + tag=tags[i]; + + for(i=0;inattributes;i++) + if(attributes[i]) + { + free(attributes[i]); + attributes[i]=NULL; + } + + break; + } + + if(tag==NULL) + { + fprintf(stderr,"XML Parser: Error on line %d: unexpected tag '%s'.\n",yylineno,yylval); + yychar=LEX_ERROR_UNEXP_TAG; + } + + break; + + /* The end of the start-tag for an element */ + + case LEX_TAG_PUSH: + + if(stackused==stackdepth) + { + tag_stack =(xmltag**) realloc((void*)tag_stack ,(stackdepth+=8)*sizeof(xmltag*)); + tags_stack=(xmltag***)realloc((void*)tags_stack,(stackdepth+=8)*sizeof(xmltag**)); + } + + tag_stack [stackused]=tag; + tags_stack[stackused]=tags; + stackused++; + + if(tag->callback) + if(call_callback(tag->name,tag->callback,XMLPARSE_TAG_START,tag->nattributes,attributes)) + yychar=LEX_ERROR_CALLBACK; + + tags=tag->subtags; + + break; + + /* The end of the empty-element-tag for an XML declaration */ + + case LEX_XML_DECL_FINISH: + + /* The end of the empty-element-tag for an element */ + + case LEX_TAG_FINISH: + + if(tag->callback) + if(call_callback(tag->name,tag->callback,XMLPARSE_TAG_START|XMLPARSE_TAG_END,tag->nattributes,attributes)) + yychar=LEX_ERROR_CALLBACK; + + if(stackused>0) + tag=tag_stack[stackused-1]; + else + tag=NULL; + + break; + + /* The end of the end-tag for an element */ + + case LEX_TAG_POP: + + stackused--; + tags=tags_stack[stackused]; + tag =tag_stack [stackused]; + + if(strcmp(tag->name,yylval)) + { + fprintf(stderr,"XML Parser: Error on line %d: end tag '' doesn't match start tag '<%s ...>'.\n",yylineno,yylval,tag->name); + yychar=LEX_ERROR_UNBALANCED; + } + + if(stackused<0) + { + fprintf(stderr,"XML Parser: Error on line %d: end tag '' seen but there was no start tag '<%s ...>'.\n",yylineno,yylval,yylval); + yychar=LEX_ERROR_NO_START; + } + + for(i=0;inattributes;i++) + if(attributes[i]) + { + free(attributes[i]); + attributes[i]=NULL; + } + + if(tag->callback) + if(call_callback(tag->name,tag->callback,XMLPARSE_TAG_END,tag->nattributes,attributes)) + yychar=LEX_ERROR_CALLBACK; + + if(stackused>0) + tag=tag_stack[stackused-1]; + else + tag=NULL; + + break; + + /* An attribute key */ + + case LEX_ATTR_KEY: + + attribute=-1; + + for(i=0;inattributes;i++) + if(!strcasecmp(yylval,tag->attributes[i])) + { + attribute=i; + + break; + } + + if(attribute==-1) + { + if((options&XMLPARSE_UNKNOWN_ATTRIBUTES)==XMLPARSE_UNKNOWN_ATTR_ERROR || + ((options&XMLPARSE_UNKNOWN_ATTRIBUTES)==XMLPARSE_UNKNOWN_ATTR_ERRNONAME && !strchr(yylval,':'))) + { + fprintf(stderr,"XML Parser: Error on line %d: unexpected attribute '%s' for tag '%s'.\n",yylineno,yylval,tag->name); + yychar=LEX_ERROR_UNEXP_ATT; + } + else if((options&XMLPARSE_UNKNOWN_ATTRIBUTES)==XMLPARSE_UNKNOWN_ATTR_WARN) + fprintf(stderr,"XML Parser: Warning on line %d: unexpected attribute '%s' for tag '%s'.\n",yylineno,yylval,tag->name); + } + + break; + + /* An attribute value */ + + case LEX_ATTR_VAL: + + if(tag->callback && attribute!=-1 && yylval) + attributes[attribute]=strcpy(malloc(strlen(yylval)+1),yylval); + + break; + + /* End of file */ + + case LEX_EOF: + + if(tag) + { + fprintf(stderr,"XML Parser: Error on line %d: end of file seen without end tag ''.\n",yylineno,tag->name); + yychar=LEX_ERROR_UNEXP_EOF; + } + + break; + + case LEX_ERROR_TAG_START: + fprintf(stderr,"XML Parser: Error on line %d: character '<' seen not at start of tag.\n",yylineno); + break; + + case LEX_ERROR_XML_DECL_START: + fprintf(stderr,"XML Parser: Error on line %d: characters ''.\n",yylineno,tag->name); + break; + + case LEX_ERROR_XML_DECL: + fprintf(stderr,"XML Parser: Error on line %d: invalid character seen inside XML declaration ''.\n",yylineno,tag->name); + break; + + case LEX_ERROR_ATTR: + fprintf(stderr,"XML Parser: Error on line %d: invalid attribute definition seen in tag.\n",yylineno); + break; + + case LEX_ERROR_END_TAG: + fprintf(stderr,"XML Parser: Error on line %d: invalid character seen in end-tag.\n",yylineno); + break; + + case LEX_ERROR_COMMENT: + fprintf(stderr,"XML Parser: Error on line %d: invalid comment seen.\n",yylineno); + break; + + case LEX_ERROR_CLOSE: + fprintf(stderr,"XML Parser: Error on line %d: character '>' seen not at end of tag.\n",yylineno); + break; + + case LEX_ERROR_ATTR_VAL: + fprintf(stderr,"XML Parser: Error on line %d: invalid character '%s' seen in attribute value.\n",yylineno,yylval); + break; + + case LEX_ERROR_ENTITY_REF: + fprintf(stderr,"XML Parser: Error on line %d: invalid entity reference '%s' seen in attribute value.\n",yylineno,yylval); + break; + + case LEX_ERROR_CHAR_REF: + fprintf(stderr,"XML Parser: Error on line %d: invalid character reference '%s' seen in attribute value.\n",yylineno,yylval); + break; + } + } + while(yychar>LEX_EOF && yychar"); + if(!strcmp(string,"'")) return("'"); + if(!strcmp(string,""")) return("\""); + return(NULL); +} + + +/*++++++++++++++++++++++++++++++++++++++ + Convert an XML character reference into an ASCII string. + + char *ParseXML_Decode_Char_Ref Returns a pointer to the replacement decoded string. + + const char *string The character reference string. + ++++++++++++++++++++++++++++++++++++++*/ + +char *ParseXML_Decode_Char_Ref(const char *string) +{ + static char result[2]=" "; + long int val; + + if(string[2]=='x') val=strtol(string+3,NULL,16); + else val=strtol(string+2,NULL,10); + + if(val<0 || val>255) + return(NULL); + + result[0]=val&0xff; + + return(result); +} + + +/*++++++++++++++++++++++++++++++++++++++ + Convert a string into something that is safe to output in an XML file. + + char *ParseXML_Encode_Safe_XML Returns a pointer to the replacement encoded string (or the original if no change needed). + + const char *string The string to convert. + ++++++++++++++++++++++++++++++++++++++*/ + +char *ParseXML_Encode_Safe_XML(const char *string) +{ + static const char hexstring[17]="0123456789ABCDEF"; + int i=0,j=0,len; + char *result; + + for(i=0;string[i];i++) + if(string[i]=='<' || string[i]=='>' || string[i]=='&' || string[i]=='\'' || string[i]=='"' || string[i]<32 || string[i]>126) + break; + + if(!string[i]) + return((char*)string); + + len=i+256-6; + + result=(char*)malloc(len+7); + strncpy(result,string,j=i); + + do + { + for(;j') + { + result[j++]='&'; + result[j++]='g'; + result[j++]='t'; + result[j++]=';'; + } + else if(string[i]=='&') + { + result[j++]='&'; + result[j++]='a'; + result[j++]='m'; + result[j++]='p'; + result[j++]=';'; + } + else if(string[i]=='\'') + { + result[j++]='&'; + result[j++]='a'; + result[j++]='p'; + result[j++]='o'; + result[j++]='s'; + result[j++]=';'; + } + else if(string[i]=='"') + { + result[j++]='&'; + result[j++]='q'; + result[j++]='u'; + result[j++]='o'; + result[j++]='t'; + result[j++]=';'; + } + else if(string[i]<32 || string[i]>126) + { + result[j++]='&'; + result[j++]='#'; + result[j++]='x'; + result[j++]=hexstring[(string[i]&0xf0)>>4]; + result[j++]=hexstring[ string[i]&0x0f ]; + result[j++]=';'; + } + else + result[j++]=string[i]; + + if(string[i]) /* Not finished */ + { + len+=256; + result=(char*)realloc((void*)result,len+7); + } + } + while(string[i]); + + result[j]=0; + + return(result); +} + + +/*++++++++++++++++++++++++++++++++++++++ + Convert a string to a integer (checking that it really is a integer). + + int ParseXML_GetInteger Returns 1 if a integer could be found or 0 otherwise. + + const char *string The string to be parsed. + + int *number Returns the number. + ++++++++++++++++++++++++++++++++++++++*/ + +int ParseXML_GetInteger(const char *string,int *number) +{ + const char *p=string; + + if(*p=='-' || *p=='+') + p++; + + while(isdigit(*p)) + p++; + + if(*p) + return(0); + + *number=atoi(string); + + return(1); +} + + +/*++++++++++++++++++++++++++++++++++++++ + Convert a string to a floating point number (checking that it really is a number). + + int ParseXML_GetFloating Returns 1 if a number could be found or 0 otherwise. + + const char *string The string to be parsed. + + int *number Returns the number. + ++++++++++++++++++++++++++++++++++++++*/ + +int ParseXML_GetFloating(const char *string,double *number) +{ + const char *p=string; + + if(*p=='-' || *p=='+') + p++; + + while(isdigit(*p) || *p=='.') + p++; + + if(*p=='e' || *p=='E') + { + p++; + + if(*p=='-' || *p=='+') + p++; + + while(isdigit(*p)) + p++; + } + + if(*p) + return(0); + + *number=atof(string); + + return(1); +} diff --git a/web/INSTALL.txt b/web/INSTALL.txt new file mode 120000 index 0000000..4c0523b --- /dev/null +++ b/web/INSTALL.txt @@ -0,0 +1 @@ +../doc/INSTALL.txt \ No newline at end of file diff --git a/web/data/create.sh b/web/data/create.sh new file mode 100755 index 0000000..e1d5a81 --- /dev/null +++ b/web/data/create.sh @@ -0,0 +1,30 @@ +#!/bin/sh -x + +# This script can download either from GeoFabrik or Cloudmade. + + +# EDIT THIS to set the names of the files to download. +files="europe/great_britain.osm.bz2 europe/ireland.osm.bz2 europe/isle_of_man.osm.bz2" + +# Download the files + +for file in $files; do + wget -N http://download.geofabrik.de/osm/$file +done + + +## EDIT THIS to set the names of the files to download. +#files="europe/united_kingdom/united_kingdom.osm.bz2 europe/ireland/ireland.osm.bz2 europe/isle_of_man/isle_of_man.osm.bz2" +# +## Download the files +# +#for file in $files; do +# wget -N http://downloads.cloudmade.com/$file +#done + + +# Process the data + +bunzip2 *.bz2 + +../bin/planetsplitter *.osm diff --git a/web/www/openlayers/install.sh b/web/www/openlayers/install.sh new file mode 100755 index 0000000..60a6eee --- /dev/null +++ b/web/www/openlayers/install.sh @@ -0,0 +1,25 @@ +#!/bin/sh -x + +version=2.9.1 + +# Download the file. + +wget http://openlayers.org/download/OpenLayers-$version.tar.gz + +# Uncompress it. + +tar -xzf OpenLayers-$version.tar.gz + +# Create a custom OpenLayers file + +(cd OpenLayers-$version/build && python build.py ../../routino.cfg && cp OpenLayers.js ..) + +# Copy the files. + +cp -p OpenLayers-$version/OpenLayers.js . +cp -pr OpenLayers-$version/img . +cp -pr OpenLayers-$version/theme . + +# Delete the remainder + +rm -rf OpenLayers-$version diff --git a/web/www/openlayers/routino.cfg b/web/www/openlayers/routino.cfg new file mode 100644 index 0000000..6f6226e --- /dev/null +++ b/web/www/openlayers/routino.cfg @@ -0,0 +1,44 @@ +# This is a configuration file to allow building an optimised OpenLayers +# Javascript file that contains all of the features required for Routino. + +[first] +OpenLayers/SingleFile.js +OpenLayers.js +OpenLayers/BaseTypes.js +OpenLayers/BaseTypes/Class.js +OpenLayers/Util.js +Rico/Corner.js + +[last] + +[include] +OpenLayers/Ajax.js +OpenLayers/BaseTypes/LonLat.js +OpenLayers/BaseTypes/Bounds.js +OpenLayers/Control/DragFeature.js +OpenLayers/Control/LayerSwitcher.js +OpenLayers/Control/Navigation.js +OpenLayers/Control/PanZoomBar.js +OpenLayers/Control/ScaleLine.js +OpenLayers/Feature/Vector.js +OpenLayers/Format/GML.js +OpenLayers/Format/GPX.js +OpenLayers/Geometry/LineString.js +OpenLayers/Geometry/Point.js +OpenLayers/Geometry/Polygon.js +OpenLayers/Layer/Boxes.js +OpenLayers/Layer/GML.js +OpenLayers/Layer/SphericalMercator.js +OpenLayers/Layer/TMS.js +OpenLayers/Layer/Vector.js +OpenLayers/Map.js +OpenLayers/Marker/Box.js +OpenLayers/Projection.js +OpenLayers/Renderer/Elements.js +OpenLayers/Renderer/Canvas.js +OpenLayers/Renderer/SVG.js +OpenLayers/Renderer/VML.js +OpenLayers/Rule.js +OpenLayers/Style.js + +[exclude] diff --git a/web/www/routino/.htaccess b/web/www/routino/.htaccess new file mode 100644 index 0000000..29ea962 --- /dev/null +++ b/web/www/routino/.htaccess @@ -0,0 +1,40 @@ +## +## Options for Apache web server for language specific web pages and to run +## Routino CGI scripts. +## + +# The translated router pages use the MultiViews option to serve up a version of +# the web page depending on the client language preference. If the line below +# is used in a .htaccess file like this one and the "AllowOverride none" option +# is set in the main Apache configuration file then the entry in the .htaccess +# file will not work. + +#Options +MultiViews + +# The English language option will be served if there is no other version +# present and no errors will be returned to the user in case of problems + +LanguagePriority en +ForceLanguagePriority Prefer Fallback + +# The Routino CGI scripts are stored in this directory and use the filename +# extension ".cgi". This filename extension needs to be registered with Apache +# for the scripts to be executed. + +AddHandler cgi-script .cgi + +# The ExecCGI option must be set for the CGIs in this directory to be executed +# by Apache. If the line below is used in a .htaccess file like this one and +# the "AllowOverride none" option is set in the main Apache configuration file +# then the entry in the .htaccess file will not work. + +#Options +ExecCGI + +# The CGI scripts that are used by Routino also call some other Perl scripts, to +# stop these scripts from being seen by web users they can be denied by the +# following entry. + + + Order deny,allow + Deny from all + diff --git a/web/www/routino/customrouter.cgi b/web/www/routino/customrouter.cgi new file mode 100755 index 0000000..454818e --- /dev/null +++ b/web/www/routino/customrouter.cgi @@ -0,0 +1,143 @@ +#!/usr/bin/perl +# +# Routino router custom link CGI +# +# Part of the Routino routing software. +# +# This file Copyright 2008-2010 Andrew M. Bishop +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# + +# Use the generic router script +require "router.pl"; + +# Use the perl CGI module +use CGI ':cgi'; + +# Create the query and get the parameters + +$query=new CGI; + +@rawparams=$query->param; + +# Legal CGI parameters with regexp validity check + +%legalparams=( + "lon" => "[-0-9.]+", + "lat" => "[-0-9.]+", + "zoom" => "[0-9]+", + + "lon[1-9]" => "[-0-9.]+", + "lat[1-9]" => "[-0-9.]+", + "transport" => "[a-z]+", + "highway-[a-z]+" => "[0-9.]+", + "speed-[a-z]+" => "[0-9.]+", + "property-[a-z]+" => "[0-9.]+", + "oneway" => "(1|0|true|false|on|off)", + "weight" => "[0-9.]+", + "height" => "[0-9.]+", + "width" => "[0-9.]+", + "length" => "[0-9.]+", + + "language" => "[-a-zA-Z]+" + ); + +# Validate the CGI parameters, ignore invalid ones + +foreach $key (@rawparams) + { + foreach $test (keys (%legalparams)) + { + if($key =~ m%^$test$%) + { + $value=$query->param($key); + + if($value =~ m%^$legalparams{$test}$%) + { + $cgiparams{$key}=$value; + last; + } + } + } + } + +# Fill in the default parameters + +%fullparams=FillInDefaults(%cgiparams); + +# Open template file and output it + +$lang=$cgiparams{'language'}; + +if( -f "router.html.$lang") + { + open(TEMPLATE,") + { + if(m%^%) + { + s/'lat'/$cgiparams{'lat'}/ if(defined $cgiparams{'lat'}); + s/'lon'/$cgiparams{'lon'}/ if(defined $cgiparams{'lon'}); + s/'zoom'/$cgiparams{'zoom'}/ if(defined $cgiparams{'zoom'}); + print; + } + elsif(m%%) + { + $key=$1; + + m%type="([a-z]+)"%; + $type=$1; + + m%value="([a-z]+)"%; + $value=$1; + + if($type eq "radio") + { + $checked=""; + $checked="checked" if($fullparams{$key} eq $value); + + s%>% $checked>%; + } + elsif($type eq "checkbox") + { + $checked=""; + $checked="checked" if($fullparams{$key}); + + s%>% $checked>%; + } + elsif($type eq "text") + { + s%>% value="$fullparams{$key}">%; + } + + print; + } + else + { + print; + } + } + +close(TEMPLATE); diff --git a/web/www/routino/customvisualiser.cgi b/web/www/routino/customvisualiser.cgi new file mode 100755 index 0000000..4b28e57 --- /dev/null +++ b/web/www/routino/customvisualiser.cgi @@ -0,0 +1,82 @@ +#!/usr/bin/perl +# +# Routino data visualiser custom link CGI +# +# Part of the Routino routing software. +# +# This file Copyright 2008,2009 Andrew M. Bishop +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# + +# Use the perl CGI module +use CGI ':cgi'; + +# Create the query and get the parameters + +$query=new CGI; + +@rawparams=$query->param; + +# Legal CGI parameters with regexp validity check + +%legalparams=( + "lon" => "[-0-9.]+", + "lat" => "[-0-9.]+", + "zoom" => "[0-9]+" + ); + +# Validate the CGI parameters, ignore invalid ones + +foreach $key (@rawparams) + { + foreach $test (keys (%legalparams)) + { + if($key =~ m%^$test$%) + { + $value=$query->param($key); + + if($value =~ m%^$legalparams{$test}$%) + { + $cgiparams{$key}=$value; + last; + } + } + } + } + +# Open template file and output it + +open(TEMPLATE,") + { + if(m%^%) + { + s/'lat'/$cgiparams{'lat'}/ if(defined $cgiparams{'lat'}); + s/'lon'/$cgiparams{'lon'}/ if(defined $cgiparams{'lon'}); + s/'zoom'/$cgiparams{'zoom'}/ if(defined $cgiparams{'zoom'}); + print; + } + else + { + print; + } + } + +close(TEMPLATE); diff --git a/web/www/routino/icons/ball-0.png b/web/www/routino/icons/ball-0.png new file mode 100644 index 0000000000000000000000000000000000000000..d54adaa0ec050db9dea355e4640113c75ffea96d GIT binary patch literal 128 zcmeAS@N?(olHy`uVBq!ia0vp^oFL4>1|%O$WD@{VY)RhkE)4%caKYZ?lYt_po-U3d z5|@+z{QqyyEadRa;E;q)gvv!z10zEL#u)`Onv#?*stGRbnAjloNQRG_X+~zmVNQm> XMx4Thzc=y$4Pfwe^>bP0l+XkKL>nb| literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/ball-1.png b/web/www/routino/icons/ball-1.png new file mode 100644 index 0000000000000000000000000000000000000000..fba4e314b8eedb2cdf431ed055049865bf723193 GIT binary patch literal 129 zcmeAS@N?(olHy`uVBq!ia0vp^oFL4>1|%O$WD@{VY)RhkE)4%caKYZ?lYt^;o-U3d z5|@`wFyvz};9y$#|NrOG&1<_=_WCNg87??v$GjuufvNR_r3}i3t&4mUU;KLX`NM+c ZymxO)FzJ3{%mkXi;OXk;vd$@?2>=3uD+mAp literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/ball-2.png b/web/www/routino/icons/ball-2.png new file mode 100644 index 0000000000000000000000000000000000000000..e664ba68fa498b52fc896aabc0240c0f3bc14d76 GIT binary patch literal 133 zcmeAS@N?(olHy`uVBq!ia0vp^oFL4>1|%O$WD@{VY)RhkE)4%caKYZ?lYt^uo-U3d z5|@+z{Qqyy%w(Wc_|N!IgwBjfFQon%8yN~PO8ozy{=?rxFi=f!HOE8lHw_-`n-{ck cek)*N5aJf#H&`#04>X0r)78&qol`;+0B?#Yr2qf` literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/ball-3.png b/web/www/routino/icons/ball-3.png new file mode 100644 index 0000000000000000000000000000000000000000..0b8c2bf3c602c6b7ce1794e5efe6f16f4117da23 GIT binary patch literal 130 zcmeAS@N?(olHy`uVBq!ia0vp^oFL4>1|%O$WD@{VY)RhkE)4%caKYZ?lYt`Uo-U3d z5|@`waO7i9;5cmc|Nrx1|%O$WD@{VY)RhkE)4%caKYZ?lYt_(o-U3d z5|@+z{Qqyy%=2YO=K)LA-S-R*-RSxM|G)c07j_WO$WVY$Vs~uof8ijPDV|3(oxZWA d@~NxMVF*x{mYdA1|%O$WD@{VY)RhkE)4%caKYZ?lYt`Eo-U3d z5|@`wFyv!U;9!o9KX(3W`g4^)anC)y+Y|$sqbfAmo&01i&PY4uX(g0aw0h0XTKKnn c|6y%gUo|0d=Y$?BMW8VZp00i_>zopr0R0yy3jhEB literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/ball-6.png b/web/www/routino/icons/ball-6.png new file mode 100644 index 0000000000000000000000000000000000000000..01c37fedc98c1e13a31774cd7d7350e9bd320d9c GIT binary patch literal 134 zcmeAS@N?(olHy`uVBq!ia0vp^oFL4>1|%O$WD@{VY)RhkE)4%caKYZ?lYt`Eo-U3d z5|@+z{QqyyER&M!TCl9r_|S}=|NsBHPjz9xyhn1TL*r#@$Y2*E_B3Ou1|%O$WD@{VY)RhkE)4%caKYZ?lYt`Uo-U3d z5|@`waO7i9;5Z!i|Nrx1|%O$WD@{VY)RhkE)4%caKYZ?lYt`Uo-U3d z5|@`waO7i9;5Z!i|Nrx1|%O$WD@{VY)RhkE)4%caKYZ?lYt`Uo-U3d z5|@`waO7i9;5Z!i|Nrx. +# + +use Graphics::Magick; + +# Markers for routing + +@names=("red","grey"); +@borders=("black","grey"); +@letters=("red","grey"); + +foreach $character ('0'..'9','home') + { + foreach $colour (0..$#names) + { + $image=Graphics::Magick->new; + $image->Set(size => "63x75"); + + $image->ReadImage('xc:white'); + $image->Transparent('white'); + + $image->Draw(primitive => polygon, points => '1,32 32,73 61,32 32,10', + stroke => $borders[$colour], fill => 'white', strokewidth => 6, + antialias => 'false'); + + $image->Draw(primitive => arc, points => '1,1 61,61 -180,0', + stroke => $borders[$colour], fill => 'white', strokewidth => 6, + antialias => 'false'); + + if($character eq 'home') + { + $home=Graphics::Magick->new; + + $home->ReadImage("home.png"); + + $home->Opaque(fill => $names[$colour], color => 'black'); + + $image->Composite(image => $home, compose => Over, + x => 32-$home->Get('width')/2, y => 26-$home->Get('height')/2); + } + else + { + ($x_ppem, $y_ppem, $ascender, $descender, $width, $height, $max_advance) = + $image->QueryFontMetrics(text => $character, font => 'Helvetica', pointsize => '36'); + + $image->Annotate(text => $character, font => 'Helvetica', pointsize => '36', + stroke => $letters[$colour], fill => $letters[$colour], + x => 32, y => 32-$descender, align => Center, + antialias => 'false'); + } + + $image->Resize(width => 21, height => 25); + + $image->Write("marker-$character-$names[$colour].png"); + + undef $image; + } + } + +# Balls for visualiser descriptions + +@colours=("#FFFFFF", + "#FF0000", + "#FFFF00", + "#00FF00", + "#8B4513", + "#00BFFF", + "#FF69B4", + "#000000", + "#000000", + "#000000"); + +foreach $colour (0..9) + { + $image=Graphics::Magick->new; + $image->Set(size => "9x9"); + + $image->ReadImage('xc:white'); + $image->Transparent('white'); + + $image->Draw(primitive => circle, points => '4,4 4,8', + fill => $colours[$colour], stroke => $colours[$colour], + antialias => 'false'); + + $image->Write("ball-$colour.png"); + + undef $image; + } + +# Limit signs + +foreach $limit (1..160) + { + &draw_limit($limit); + } + +foreach $limit (10..200) + { + &draw_limit(sprintf "%.1f",$limit/10); + } + +&draw_limit("no"); + +unlink "limit-0.png"; +link "limit-no.png","limit-0.png"; + +unlink "limit-0.0.png"; +link "limit-no.png","limit-0.0.png"; + +sub draw_limit + { + ($limit)=@_; + + $image=Graphics::Magick->new; + $image->Set(size => "57x57"); + + $image->ReadImage('xc:white'); + $image->Transparent('white'); + + $image->Draw(primitive => circle, points => '28,28 28,55', + stroke => 'red', fill => 'white', strokewidth => 3, + antialias => 'false'); + + ($x_ppem, $y_ppem, $ascender, $descender, $width, $height, $max_advance) = + $image->QueryFontMetrics(text => "$limit", font => 'Helvetica', pointsize => '22'); + + $image->Annotate(text => "$limit", font => 'Helvetica', pointsize => '22', + stroke => 'black', fill => 'black', + x => 28, y => 28-$descender, align => Center, + antialias => 'false'); + + $image->Resize(width => 19, height => 19); + + $image->Write("limit-$limit.png"); + + undef $image; + } diff --git a/web/www/routino/icons/home.png b/web/www/routino/icons/home.png new file mode 100644 index 0000000000000000000000000000000000000000..1a7c261fe6a135592bb7e3df4e672caa4b14114c GIT binary patch literal 233 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE0wix1Z>k4UjKx9jP7LeL$-D$|I14-?iy0WW zg+Z8+Vb&Z8pdfpRr>`sfEoN2`el^Ffy;Ffg)t)YnAs)xCUOCOzpuppDacA5A|EYH* z9y4#yb(z9D+p=XF4*2&8 z*n2e9Y;XCw?B#<=EU__{7M+_abAGqQ`YTsi*`^rmVfPRHe2I5ctyX=eQoLT`KgQ4h an?>iY6|vrbM+@i*1_n=8KbLh*2~7Zex>4W& literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-0.0.png b/web/www/routino/icons/limit-0.0.png new file mode 100644 index 0000000000000000000000000000000000000000..cbcf3112112442f5788de2b7fbd1cbd01e250fe2 GIT binary patch literal 649 zcmV;40(Sk0P)eVzjI*i^W}LG8fc!a%_y$buyE=V6nJM%gQncVt>~PB+--Wj$(WJD-I8rVVWuA za=_#yFfaf-Jpqmb9326ThKsqmD%RJ3AeH*ntM8F*dy1xMF`p0FY?5Kfe<}>gW|Pe4 zLz<>V+n#!5yntMHl&0BWxlFd(e3#m7vRo!jvq9G#eSw9c$Wm#Og+iB}_dd|8JdZ3C zx-6A88HOTgSu@OLZ)n>e1HH<&$!zw9mNgUGcz*tdbow)jML-bnVU3IcXJ-iMA;Ft3~QMMMx4L%aP8*FoaMF*^$C1nBqAYTkB`ta4Y#+qP!t8CDB}M99?4`9ilV%Mx?Nyx4S0C?+1EYm^9A(v jHv5eVzjI*i^W}LG8fc!a%_y$buyE=V6nJM%gQncVt>~PB+--Wj$(WJD-I8rVVWuA za=_#yFfaf-Jpqmb9326ThKsqmD%RJ3AeH*ntM8F*dy1xMF`p0FY?5Kfe<}>gW|Pe4 zLz<>V+n#!5yntMHl&0BWxlFd(e3#m7vRo!jvq9G#eSw9c$Wm#Og+iB}_dd|8JdZ3C zx-6A88HOTgSu@OLZ)n>e1HH<&$!zw9mNgUGcz*tdbow)jML-bnVU3IcXJ-iMA;Ft3~QMMMx4L%aP8*FoaMF*^$C1nBqAYTkB`ta4Y#+qP!t8CDB}M99?4`9ilV%Mx?Nyx4S0C?+1EYm^9A(v jHv5*jo z_ktu|EYAy3hzyIxJ!;xHQz>$8j!dOUO*?0?xJMx}^t{lowGv4Lvh8W??0mrS@jDpC z3|3YET?bSZczgt!P2l7NsMl@ea^FxYea6hp_nH4?r~5G9msu`vvry=<-F_YDS?xAi zDD+q^Z`1c>P>3uunQNNnzk!}*nq($(O(C+OkFNU(^Yd>}ECP}g+-0p+gYWy8n3xDW z%d&8EbcB(S5ll@@_041wu&g(z)k2`ITdY=tjh>#KczAfog@py~@9zix#l;2J*4DVX zy2@NG$IHvh!Re}1Qr9gRjGM>?HGuh$EF*L9()D#pjh5sgN1e}6xKDGCq{BMi?2x?TKDByn|h zg?Ky;$8jKpz{A4>W@l#yN!>2sd61DvoT1tL4HA#X5sgOCYPGPnwS~pSMWoYdlu9LN znucsPJ4kLefkfg=;>N}o#A5HUy9>y25Wl^>MI;h|s;aoTxj{0SgzLI!wOW{*oJ1rN z=_mGjz~&}!clULu|7^$|Fx1{0;?($0^K1x{_6PmiLCK?Mlsf3-Ln)0^(HTfJtQ;m_>9@NE0XX=4>Gj>t-ccaDVWcbMHOx@!owG zB(b)9KSI;gXc*V5RPI?Ul6!k(u}D@b_cV-anx;nIkNjCHkwl2&FEO;XqUbe#OsObD5c#s%y3fV0`#j8@EMWW64+ z(YT-xGH9Ad)U*d$*1s98uq;y39%!0Jk)4 z1BSzQ==CC?st(!dgd2ID$8NXFQmMptyZx$8lI(OyRUOK3-2 zfMxr*?tu(N0d{smJsORoTCJj7E+YtnS9Ji(dPRW@-v<^8Y=R_4qY*sM+n9sJ0`Psv z$Y$?g+b`moOa{qh64U7vtyT-J>xOzFkyuZ*Z6KSylX!ai4XM;eTwMXO9L6V;3F7fM zVzC&;<1uo%927+f_1)dwWnvHjXJ^3Uh+?Y~QUM<%R&@@vl6!w`&TreIdCnm^voJ=GxSSak%G*filz^}OqN%(S{7=`Dd%3^Vw`Mk$g>usPHwOV98 z@3C0irj#mZn)6JhZfIHm26~ZYk*U-TO>;i*>FMb+CMVycPyiIguVuAbg_IJ*!@~do zDJ9O%&R`e@T-QY`7K5TFz}OhjXuLzU8US^@!Aiy7=<)H9hlhupnwny{T=wmTVKA9Y zGLy+r2*K0S)86SS6;jt5Dw@qX==!VIRTKr9rr~FKo`>b-W$f+kLDMu`Utjl}x(+m( zb1H&CARPAXp->2!Oa`;Fv+z96w=XU(VrFIrYinzmo}NZJo$kTHVIUYp5UvY!yZD<( z;_B)OmzS5=+}uPim&4N15(WncdqLeU;JQ!|jh>@k{|z!SGJ>I@A>7~JV{2;*wrv9d z48y?c>MD+pk9)!OIuMPXD_mJQfTn%G?k=FJzW?s-4g&)Nh(sc|y}d;!6hf!d@vlBQ zI{I8!&jZ%ifllXZU;kO3JD{(<*~h8zpXON~ChZT)UPuB!Vo5^)0000qJfACf`x>!NMM>-^Bcq_J78Gs zWeow#WwEkwvESWBwut@&3qgpP#+g8pUJz#y6BDp7qjTroLY(M+fD73dZgW1Kb2;~( z1Cn@gd_O|V%CJ!Q!gRXHREnIPB~vLfoo=#F_(IFd(Dx(1=1L?H%5|r(yZZsh$M2x) zNf-v8X+SIn^m;(82ArG#)vAkJ?mIR&KO>p^5$1ML9fuTxf0PiU=p7w87E|Z#8m(gx7LepNnt|Uo_$K!aeXfz7bG?B~YFg7-ZySux-Q`3NU zdr^j>0MTe@A08gY%E}5B78Vc$L1>?unZfe%G8&BrZf?X0wUQ%gcU{=K;PC8LHYqt@ayaVqyZLqoZiGTG-y+#`N?whK7c) zzP^sh$w?T7(GRZG099>BTwD8!c>F!~_5fK9{nyvm7#SHsEEdDn)fEzn1bV$5TCEmT zRqgjQ2!M?Z;P&?0K>yi*Ct#qnIl!&)pZ3`RHtkQA4o39fve{Vx0000 C5ip$q literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-1.4.png b/web/www/routino/icons/limit-1.4.png new file mode 100644 index 0000000000000000000000000000000000000000..9a4b2684ef275fec447eeab4c279094a34453f66 GIT binary patch literal 664 zcmV;J0%!e+P)LoK~y-)wUoU}BS9F4f4f;tL~@`3gMlp+1q%sl5n&t8RDx}~T)=Su zfSgPE~#1hIvp~XBXzw@+n(;pgpg?%d1f*m+wHfB`n20* zCgagC@)SY_O>>!v#5FDJ-$Z>Zi%cZ0X`0J{&(F_aFgyDm*(@MQ{#}+zB?uufF)@MH zoSmKF;^G3TszQ<^AQ}Z~wRb3$0-&bVSSa`hJv}|~@bHlH^Yh%_-ycY#(P(gWb(N`9 zisE&F3I$TrYBE}_MQGYUbtOqcI2<0x=>Gm5`FtMDW)mYLBfYHpXd2LJEy_?7AQ<%L zM@L7IN~N%{umI2V{P|+Bh+?sbxw$!9U0vb){QN~03<8P*8LkU-yZ9Rn}mwF5tS55s93jTK!EjIXQ{(@o_YpO>A#(BbiL%;NSp;VPJiI9cycA zFTvF+5Q&^f+}QYraQGv3cL7=U*Bgxn#>U1F3WacUbAxC!3Pn+HcXx+}hX+heP4xmj y57^uUZg0O2^_>m*2MqOZ4zX+er+zksPWuz@K2Al4AmZr&0000l2fc-|c$aoX;9SeE<|%MvLR zMB;HvG|iBFe(~AS6;&z|qG>rvr9|p=c@_1#NGc_wX*sD>Cb}}qQY4w&kwl^+wc6`M z&(vxni9|<|$sMsQ1u=|>gu~a8&;Og~nS5R(9KIIAh&a>j_D5!C-=gc_aM*8|&1SJI zi{as603IJ7IXXJx?Cgwkxy;nm6soF%-w)^KZ^&ky3Z+tnXcQC$fOfmh!NCC=8yjS^ zSzCT{b3;0v=J4>4OeRCK+3W@s1)@H?wAi|#rc4!mA3KT|H3X*QcUolb(m zAY)@=y2&OSkH^F6>MF5VjJ>@*PEJmGvcW-c zxlk}oXtnqoBEr?x71e5$>FH@!R#q4t9i`D|^paXFFijMEz6*-Q-y|a=BMc1QtZf=fPEY?dd7QyGcaLD@l7rfs0eEw`V0pRZL4!7Hls;b=H-s1QB zd3boBUavDgK91Y%?k09Ru(=71#@D|7vp!EiUuUzATjM|Nvp#IvAGZG^cND!}Qvd(} M07*qoM6N<$f;|v6&j0`b literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-1.6.png b/web/www/routino/icons/limit-1.6.png new file mode 100644 index 0000000000000000000000000000000000000000..1d622a7fb5d6127a4158a6e97973c4606574cd68 GIT binary patch literal 673 zcmV;S0$%-zP)xK~y-)wUoU}BT*E^&rE_7KeCbp2@z5lA_SB$f)S>16)Xh{t+D~b zHkPu6^vl{=xY)bf$QHprz%n3&jLjsFC|HPsFpI>H0UMK|;uLC`+ z(;;&?mxaO}g^)pAUtlJ4OT+j#(6bDK%w%q<>kGb*w*3i-#9QR^fFyantW+uxLLd|h zdEUjv1x`**VB0ogu^1#t0-{l%(RhPO#RsZtgQb$UQLoqI@$oUIr>A*zbmaLrH#b~c zTjT2LDmOMZcy)EvKV7Lrs%k?zBq10K;%A+mo#FEG5@TayNG6jQ85!vZ zRTXHrXJz>PfTDQ*(9jUp*VmCsrQo`*=i9c8P$-1?`FWJfWi*@3K1@*nzaKIj2k3V3 zH<85k^)(bl!T9(%R#sLJ4u@e`RzIoR1sn%5B9SKQ_1_@ja2TVbqqx7n$L{VfW@cuP zNF=bewS{CdiKV5besaAIL?TUzo10${48F&~0U*m>ywz%9cz76rKmd1lcZf!#==FMN zwOW{%nCSP+b%E_|;NjuxK;PMbf51Th<^ZS0f0}0ln6y6tp5H-OeXvQw00000NkvXX Hu0mjf0-rf@ literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-1.7.png b/web/www/routino/icons/limit-1.7.png new file mode 100644 index 0000000000000000000000000000000000000000..80ae4111f3b7c5b14343a39bca49181d5fd42228 GIT binary patch literal 659 zcmV;E0&M+>P)_E( z_ktw4j^p@fnsH__dyK^@j7G_c2{IZbW3dV|nLV0joQ~uBHCG}DPqsaZ?d^{^IY~j& z#;~*ms45T)0{8bosRW#z0fmB%<>l|#*!Y66u^(Q(N4BhaYMRMpQm|Mgh2S401X(PS z$)upBnY66=XT}rAwnwRH1?F;OyUjPL-6nH6Qqu~w?a?Qg5HhpbEvC~hTdlW&p4DoR z>9osic8fyDplL2J9>1bx{Tt|6mPN+nS2WE9->1jN&zPKik4y%TB(Il-VL%9hp`jrF zK)qha;o%|9&(Be*R1gY(qMEd~b%yZh<7!1_9H tclWKQ|E$Lo(9_xM;nw(1`>Y3>_9u{I3%D!qCyG`&j~S1XqoZUzP9~Gb%;&eM>uEZU@7G+3Bs`hsFg71jN&lnqfhkPE8B=0V()hdJ#=Z{k4Bnjbg7%$asx6#wngH$SozP>)(-Q9JZ zstQ=vlnlQg2n0NPZ*MOa78WouF#*?gJ^RJQ1tO6M4i67uSr$T}PzM$W0DeDYI1bQm z<8LB~lamwd@9$%JdKznMYfuygr>Cbc+Xfs5GNREVG#bA_1_lP8CObr91ax&aySO#}(?09Mru_+|oJmLcU%`_A0000< KMNUMnLSTYz@+-Ii literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-1.9.png b/web/www/routino/icons/limit-1.9.png new file mode 100644 index 0000000000000000000000000000000000000000..dd9370a16e4b7586e5d7c971dd2e4e5bac4c702b GIT binary patch literal 670 zcmV;P0%84$P)z1ce}qf`f$1aKbGqP7Y4umIe%6 z9HoZrrGtZmrK{L3(joXah$4iXAa4RuH+2xMh=z+eXw1F$?O?ss{s0T<7vAOh@p)d} z=Y1ZK#IxghA=hg?`OdNFtEq3}Jo!9d>u$ zz_NysP6LJk=sIwJ4^*qb{ytDHJD8dIij|d*7#{u>WT3Pa-1PrR+)tY*=X=ZYBb0~fwZhL9cSnfCZ)=JevR3zPuG1J=t-_i zX0tx?`87(Zg0?+P)4ZS%{|0)J5Tt2d(6*;T?^~@87#n+yTnCMLMGwH4S84-c76 zr#U}A&)L~oo}QkzZ&xgmhEY>duTQ}+o^@AI6htBs{H&9c6ZG}V zZx3^Gb1+S_9bByf(dd!F#l_EvMBd`lCqPvL|JBtMdU|@G>pCtkFAC;$Ke07*qoM6N<$ Ef*_AFAOHXW literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-1.png b/web/www/routino/icons/limit-1.png new file mode 100644 index 0000000000000000000000000000000000000000..328b0637fe2990e04d9b5bdf7eb0059693fe4134 GIT binary patch literal 534 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 M07*qoM6N<$f{$BId;kCd literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-10.5.png b/web/www/routino/icons/limit-10.5.png new file mode 100644 index 0000000000000000000000000000000000000000..ad68e702dd0bc6f9d132f1964aae53567453a570 GIT binary patch literal 764 zcmV}LO0M2Kv?~U`2#rG8gP|ZQWEBktj7e+r<=sJ)ZS2rO_Q3D> zKBwRNB8h)oqv4g~#Fa|DP$cqRgM-rT+tT2m6p6f7D)mB+6IY|*{dd+Qi5p6#FwdU- z%&S*FV%q~ecmS~&m?nJs1iQQN<_&CZl}IF3NvD5hVBpW2{Ea!EpO9@knwxXAvm?2# z{u9@gc6Ow>IajvrD4(CWc3g={rLb&!OG`^qrJ}E*QjwOHB-`FnsT97Fxvrtb#iyE| zZ|dyq>qOU_ok{caO)V}ymFpVFaqcS~KT{_^F^6Ykvkfm8}S z9-A$oTU}Iwgzu!-9Z!Z9~T8;Jfb+)&+DHe--fA1dVhY#P8&3aKRR?y3r zx|Ed3WHd4|qSe(^9UdNPdU{&PWKxMlLi_vslBB)8Jw+lB&CJYbVPQc>A3sW&j1-F% z49exZOiZ9g1CPf;AP~SX405>~?d|QXt*v2M7P(vwfMT&owOYmN^%99h=nV!T8ijKC zu7S@7ZEdvqe9X?yGCn>|v)SbG@)FZD>FVmDv$K<0tpj$H(;d_j7S^L8Vfmudj~|(}c&5 u;r#sfmi}3b8_?2jws1B6r#)-I(*6Z{n_fucAFs{;0000KoCK2&?yZh zn~O^m+-pY%`v-JXko*7#hky`rH+TjT1&3lhB?TcQA~`j8m$!o$+S0*7`oK4S&+zen zFG%9Iv8*mCN`%SeE<>Snj*O6_qvXg4848^y-oB= ztwzS?2{;_c<#MoX8(yy$ z#bOc1$H(aI?#9s2Q2Wjo7Z*4>I)Z7M5G4tYQt3T%xh_zWiab1IlVm27;rRGC_xJay z>pGW~mbkdM$Y?amtE(%LWVu}C%E}6tmzTM=w#M(**JLI`N>WikwK|QN8PKxea5&K4 z-w#0$psFfdE*B0C4&d>4psFeWKvh*-US7iE@gNus;$3eq5Do*?>a+l-6X@wdkJE|O z)m2PRPNLOnq1kN0?RH~ua1ecceJ~6I0AQLXyk0M6XJ?VkW>G2@f!4X5w0-m4o zDi73Rvk6!h1o-_W6bfyT&*y{N z?S?GNn3$MADwRSw9LCt#7`C>yFf}!WKp=qi^>qY;L41rxfzwmK?=OLg#2z;`+KIor zyW_*d14+^_4C=a0!!T%?CU0+VX__V*jRwo*G8>Hs*=ms+8)PD}*U_AH_yaonn;mS8 e|I}w4Xxg7!QG`-dWnJ(900006eN|OBx!I9z2kShNNh;q;&eF949WUx zIyjIP7ChOuqkMk)-f<_YR4m!{zE)SIt1EpKS69;Ns$|>ys#L5yndcc=S$U?VrG_pq zzfN?|<)yT=)X>VxGkKnY9OseZ@ej)9zfE*cJ}<@NALKZX+J61=D;m^l z51F1uu8Ys-BODH67zVjqj`sF;Ha9m3g+ktYazyr)oTih6r{ z2?m4Mw$1qXIGId_si`SOM@Ly(TccX7a&&Y=B9Xu}&AU*c0FlUhpPoGVgK+p~Uc3Oq z;Oy*-j*bpOp%C?Yo%8c^`uh4XO_OT1%D}(?7Z(?Fc6JgDhoRAcr%&P2r$1ZTvlf3q kOMkP4tMQ%otOZN^H^&xR1c3b9a{vGU07*qoM6N<$f-@Cp5dZ)H literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-10.8.png b/web/www/routino/icons/limit-10.8.png new file mode 100644 index 0000000000000000000000000000000000000000..8f6028562e1a7f0cd48a0208deb9b662b6eff410 GIT binary patch literal 756 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 FV1h!9GkyR7 literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-102.png b/web/www/routino/icons/limit-102.png new file mode 100644 index 0000000000000000000000000000000000000000..5ab7555c88039e0eaeec245e2e153a9144a697cb GIT binary patch literal 728 zcmV;}0w?{6P)*tMtD4RKC%U28lrovGa^39E%b!1AasU2fibaS-0I1b! zgki|!Fbo=v2GMAg-Q8Wr$H%GH>l6wFwzs!2O_S5p)BZ}>HhA6xgW+L_MgbTd9VMU7 zlS-xN^?LZePb?N=Vq$`LJWe*7C6!9Cv9ZCz!UDNmt}ly5VR#sWAb@U{KaqrGS$Lj@ z>$-HiT~1C;D3{ADEiF+hl^7cv>tF76AqX&-n)*zu^_wV}OcIO5a2$ul#YM{HGU;?0 z%d!Z9fNHf$sZ`?d@UTD7YQfah=ZKy@eUE8AAVW4|9-~y zqTp6;#8D`B?9fa1-n%J?gMUB})zq;k3Unw|Zq$w}$S6KQf%nw|ZqT<*ObC#hz$=f`Y75*@157;oSH z${)1w%2U%8G`}>llMx&vF zg9A-XO=)*`SC^NUT3lSz!oq^)=jU~Jc&JP!qvhpgrBW#!A0M}0x4$o0R@tClPheS{ z$_4@fqR}XZVQ_kSN+=X!Z*Px*fdO3CWo>PZY&Of_;2;kV5ABw)EU4EL2EibNLICvj z^|7?H#LUbLtyYUhqd_9X2*L7#bSl>guX}+4G>;#K1HwxbAnN(a}-D;V`ysGd(>` zKA$I%NU*uN3BdaLIujEUq|@nkz;(eiD*?TD@ek4HOWwT$!vNs=`kF{2()pKKt;X2c z7?DVX`}=!pwHl^r5{*Wo)q+>A;LDePyV_@6o`9~-W*4`{|FqA#uxbATH=9u)bfA;y P00000NkvXXu0mjfpx#;% literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-104.png b/web/www/routino/icons/limit-104.png new file mode 100644 index 0000000000000000000000000000000000000000..5e85bda129eafe07d15327fa65fe02fdd96dae28 GIT binary patch literal 733 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 U)~=g7qW}N^07*qoM6N<$f-k^W_W%F@ literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-109.png b/web/www/routino/icons/limit-109.png new file mode 100644 index 0000000000000000000000000000000000000000..fea781d6a4cff70ba028359e5f4bb85429c799a2 GIT binary patch literal 736 zcmV<60w4W}P)T zhPybHM%*uV?9kFxS~mp|{{oRpA$YPJ18GMQETn^mC;_cWPTo5Ng_aJzko&;5eec5u z-|zP#iJwlZ)g#|`mCJq5^z>JajY;XWG&UwpPk&V|_d&kzs@3ZGH5-w{J(bERZ{NP= z)2A1BUW%tr!EwMe;l~dsmEiMdI6A8E{P`C7{5w*q@Avt8a}Ye1=lNP*4t0Dig`s{+ z7)r;-((-aB&+`=okMA5EQKd2}&pXokx^#J|KjQLIT3?qu??{!(Xh#-?hSt{JXl13X zTJ6t7chqXq%1T>nYi|^W2J-zUa@}tVg8wGEBM2nd{U+ak((`w-`40~tJ|mZdNCbdF zp+FdhB$G+X0aGd*} zjYJ|O5(x~$pja#tkH^{G-lo65pUurplF1|+8yiHUQHsT4XCxd4s?|pZ(I~{@0QC0u zvb40sjS{8Rg4T-WUcN+q!EFA=?b`H@88FW$cg!vNs?{2a@&=YR0X1G>7KU2Kj2sn5F5wEqHLlT(xI SdX9Gh0000xK~y-)wUj+eTVWW6ubf14G)d8D5d{$iC7^^vhRO}M><{e9Tlf}Ncb$>bN(>5ugEe6RQG=0YJZ%d#~y<7#hDa$Wrq*Om76q?s94 zmSw9@h(9>)MWs@=ENe&EtaN#)XK{HcWwVlH?Wk1hzL&YKp-g5`v$Hi_Ts%+oz{Q0$ zJ6lsGvnba!kZq4CkvLJI@Nc3A3I!>VIFW6SdEZs5?->|)MJfdz5BYo^*L7)cZwL4- z0~CuzHa0eBYHH%;@GumMFUaS;C>ASfeO*5!<#IU<4i0K{bya$lS5{W)4>>wI($v(H zCMPGAOeXd1^i;~_q*$zIP^pX%k0Zyy z9fe9|#K7l+=4P6GKBlLqi9{mQYBdbQc#ud1)!RNz=0ya5f}%?4hL|8&n9aA|)6HAGI8t_TV&00000NkvXX Hu0mjfx}z@C literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-11.1.png b/web/www/routino/icons/limit-11.1.png new file mode 100644 index 0000000000000000000000000000000000000000..8fa9bec63aa57248472a98e4fc9a4c1108a7b248 GIT binary patch literal 582 zcmV-M0=fN(P)mdq0rG z*7Cds$x`W=&E^ety+Y56{oX5)M2KZ&aee&-cXywmX*ryo z0jdh@?*p%|fN28v_rQ2;;o{;4E-$|$m-`v^hslOfrl#qv)dVLKQV9MLA;`&utkncH zO{Za$gN(Nz%gR#I#_V*+)ructwIVwmQq#t?tn6Eu5Hj2CE7t2im&=a@1uU0jz3#K! zzM>E^sOzV!R9 z0!jG3AL{pzjQXky*!GExcpOj^DDgO|)hdd`B7EP6EX!LuKv9&azqbd($;rHW^`S5yUzE?R*P&jZg=|5cKic&`ZsqtHU85)+rgy$1yAU7 UVZQ*VWB>pF07*qoM6N<$fCaeQ{ebcD?}0x^wr!n;VKJK(bR1F${t+Qa z$04&>LBp_U+j=jf8|1n%8b+0+64`F^S+v_^sYDt^m987>h6y3FSlnbT=d;y%UQmx# zi_GPG7K@t{LIy2sfyv|*ZTsJXde}CZOkUBl7DDeH9^PYe@)h!VK$1|YR3L=F$jAr) z{HBEvI6FIoX`1jn4>3)HRIk54r4j-)?Scmf{6X^Y@Q_neQ{3O*Cm-p(y}jTerfE{w zb!IXd8iv8IwoM)#k(zcPquHE;u7jQjNs^$dDoDcj{Xjp)_kAoaEn#P82db*#TcZK! zI?!y+$xsv^97b4CkjZ2)J39;C_aV#j6Wt?~N?~SZ1}iHon4X?SI+X$x1yB^o@I2uD z9)E)*8jS|Z62bNLHNxRA zI-O21`pdB~sO#%Mr}L?=|E$j&(AVAUV{81UKI=o%{sc68R4P58O#c7?002ovPDHLk FV1h$CFpB^H literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-11.3.png b/web/www/routino/icons/limit-11.3.png new file mode 100644 index 0000000000000000000000000000000000000000..d7af6e999bb66ba56daa4bef1eebcbb962a9a63e GIT binary patch literal 676 zcmV;V0$crwP)`;9bDWE{Ipe!~{f|)cfAk!RFAmPz&h?-r*UZ58vVY zz95Mw5qU#2J?Mfu!<2V!8*m#Shqu0>1 z7%~|^Re@j-Xt#lC6*xWyDisI0+-Iz;d_XMr)$jXe(@av+4CeEKmPHD|KOzKaS!6yh zsA&dGGud@Kh#Y5vnpR<{M7COd7OfUpDv_F2q2o+E$b^ttEUr`6JvN)q6Lo1eNnQ6? zEUr@s88nPEQ>hD@=D&%$m?oJ@UC=Pnfp_=!?=ds;3WWk7Nhp`g5JF&hco+bF%bw@q z^z;;mhll8NI*6$%B-?(0aybC1>KS);`JLqc{yt}CXSuVpLq5ve+uQy_EX!ghli|X` z0@LXb$jAtWhK8QxP$-0Yy^giDHB3%U zqOY$HOG`@_A0J01n*~f0h(>LRi;Eu-iM+w)CLqhWzP`rb;2^@`FaY>D7!2a->I!3H zV`#VAsMqU=j*cP{3IWT@!0qj)p8m5QZ$M9XvxisXKi#t)T-qOICQ<^=^s@l~0000< KMNUMnLSTYMx-*vm literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-11.4.png b/web/www/routino/icons/limit-11.4.png new file mode 100644 index 0000000000000000000000000000000000000000..95a17253ace69efe867c3764037f5ebad0090c71 GIT binary patch literal 653 zcmV;80&@L{P)AHbmy%I_ISXKlF2cL0%{t244 zjIAv|QGmHQ;Q1M-*MW-*pjx$%On%4S-Zv~S+rHmVHq8Vz&0sn$SgVmj@Q(;V)@o!r zEvRV*O*1jh7zJ5Ygql`mp+F7>d>4ZOStyX2R;6V{Mqxt8)b%1W8IS$``+~;w`(!5L zQP+zULIw?Eld5`8)BLxfF{VkX>OBo(Gw{W6zG8LtBXT)Fl2EBsAcVl;;vxY2rpGj! zO|)7qL{$}1qwxWiN&r-p21_Nrk~}**kpNv6k|beaVF4uJd7iJ|^7QnCVzG#Bw~LvX8A!mWKE2+Cj9?I$oyBZ0 zh*T>T=Rr{v z*tQMZwsCiN2fV%lt_vCA@FnVX-?X%}g!%dT2^|WB(Cv1yzrT-UGKtgEQ|P*m-Q8X6 z?Cb!h353I!U^aWieBN*TK&+wC?T$011$heJL*JdAw#JekcNO|@rJ n-hip@<`l2Sf4XN=xU@e3NGORp@MdgG00000NkvXXu0mjf2y-g} literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-11.5.png b/web/www/routino/icons/limit-11.5.png new file mode 100644 index 0000000000000000000000000000000000000000..16a06e0d8f367d1b0b3cbbe71d49bbd31e543b1a GIT binary patch literal 681 zcmV;a0#^NrP)6)h1I6bCsIA;K*-3gRlbr2&tw zf>1;D(nWBvIBUCT2g&NL6+;feOF$hAItf=rj~p09>)pK_B!{*IEu;^8!#lhW|L}hw zNaD%Zwx61oq^hn^mJb<=k=@;7EJn)mAysvSnwF$(`+x8HNW#M~I_ukV?4JwZj$m`*z^l}N|oUvV6=R3g)9 zhl-+6*AsQegUB#Cs3=9|a-?PPMOYS@%aMvwq+xVC$Q(ytHoL$~#$~noGEtpsmCR&Z zX0r=)90Am{VJ4HO)b)Q8)zNh_nLMSY4f{Xd-+w|+&s(S};PatSD8O+Xw6wGUz#sYM z<^~%Z8`$05MWs@~o1r0y$H%WwDEPs6e4p#<{7EvO&$G9;muqWlWpGYeWB8gEyd|yV=02@t^Kl11{|^(-czee(nrU P00000NkvXXu0mjf<6|{e literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-11.6.png b/web/www/routino/icons/limit-11.6.png new file mode 100644 index 0000000000000000000000000000000000000000..4337fcc172d56aae0df4cd5137e7dca15bb939a7 GIT binary patch literal 680 zcmV;Z0$2TsP)F(8DTgO@;};80YAD-uH@E}HxCbdVgTE!0B#fp_=}&%?{h z`~89>dX8zjsH#b3vwMuk&lri2({J65)Qf^#`g9{6pQbn zC}WtP2V@!Wc!0-8pjrivkAZSoM=JFV8yhY`Fbv^uCW?3S0xh-ZgHe0RN6?JK~ z$V|p&F1JO?55@uEwWia5KtZIL3q919v>PS!rk2+ zHa9mhH8q83G>WygHN;{uyi28k(^DW6sta6Q{Q|%L19o=-QADHBfFw!q`FsH2xX0tc z?d>f_M@P}=bkJxtFfuao{ATOxz{A7WzW%d5Pe5O1vyWTjKkc(VY}%jSF-|6r22MQy O00007b;2j>`_j!1E zdA|=x;@NT>A5AmOe14sYM3u1^IXq0pVq_vwWj?=7(@fKGe7|}nlJKzY0j#XN$Nqj6 znl^~3DIl2yf* z0orZ+4U#xNKZg(kdwY9ORTbmoh(I#&d#9gI{bcr=TM~rsOqW2`T5U?MBZU_6_90IUtgoArw6^gy#V0pU@(Y!y^j9= ze%#*PA`}XB-qUq~g$3a5?n_tyS(hiEtFzg~*7#3-)`h1139UU)F?y`BR{#J207*qo IM6N<$g8wckumAu6 literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-11.8.png b/web/www/routino/icons/limit-11.8.png new file mode 100644 index 0000000000000000000000000000000000000000..3c14aa90949db2891a6b7f89ffaddbda4dfeca1f GIT binary patch literal 680 zcmV;Z0$2TsP)~ScMDEbg|8b`bp&gg`Ll>Tn4dH$g$TDtcTXSd89xPX|fRwonW42jB1v?~iYI z-v=b|?76OwhLL0@v&C5KgwZHDG(<+DWGr^VOlFIQk)-STexH>{qJ?D*U}NJ0^7(hr z^+8Ne1BnC>2mp6?K&1i{3P7o3VRrT#R#rY^aPUWKzGXH|jk<0yl@ctMNg?=Ggdodh zGL;h4b%UmrAITI?l^PO&o_z zr#)t~>l8u;4P%nYDzlLZGLo2LS%a zLI~vZc@zo-2qEw$9*0z|hEXi~z(nGhySuz6xwp5+k&zMZ?Cg+F^7i(2>mvsT2OJ+C z=fc7Qr>3U(-89MleKL_amSNiy&@|9>AxRQKp%6&I^SoAmN~6(0M@I)VO@pE+_*t(% zu8(a`$ng7t&Q5gt{g|Jh$LQ!NJkNtH%g=I?%galI!(kj99l^G31cO1Ks|)b^A;Wcn zMg#wXBu-CHad>!$cs!2P)m5mfinFsb;Qk(PUC4+;s;E?2L%qGdP!#1^R#g=@H#b;Y zTf^Mk9L~?rv9z>=v9U2s&&&X(2}B}QiHnP05DLA=<|ZJ^xW2wdFc?I4cQ*jEav%`E z)zuaH`}=Wwdke>L(AU=oRZ)QDWuRXF+SWa5^98i^Hrv=5uheI4Xxd+@?N%3NL}SeW O0000s9O~gu84+29MtCCdpbxCZ40%Ke&9Pi!}IXM z`~5y3i6`SYK3bN+Y<7e3_z|N~a%6;zM#*^mh}rB0Ez6+e_&B;jG(gIHaChuz&b zFwG&PQa~aB1OmX_9Z;zN`};ttWMgXT3zn8XVrb~Q=l7C@&}o_$(`m_anUs=$L`t$; zCevw2)3hjr-pY6gvh6{dW{LSc*=X=tG#X?+Pnu?lwmtX|CZ)<;ZiShQ%X)fXrhePAL{<@PpzkSr7m93LO&*47sJNN;X#dJj1`IAAK3 z;_U1!r>Cd+?d*&!6v#xPs-jklLDxaYfubk~27@38*L6MpnB(JP^!4>&eSIC8rr~R; z1WZl>wOUMtrU8CG{F;Wjxj9TsOu%(rsH*xzx6pMR*VorrSXh7%0?lR<=Qc6G$mBHa3QEIE>TNQ@k1;epqP7fr?P*5S5B&8We*1y1LNY+Y11m4g>KOztqoN62}`2~U5LPQ}=H1p@{m8EtSJQr918+XtynFE5|5yL$xF1QZ3oVp$fXlqeR9u^Irdy$wvK zA7EK2P}8Oy4kM4P*K3YOBi8G64h92~40XTXe>>f9NNU9NxPh7v8-G zBr$SaH$=lou~0ass@^b}Bv)3*WRg_X8x{)3G>jBoH}re0L=r>Ub^?coUvPQ(3A(<7 z?QKBQ07U_wpMh2jxVi%BbsL#X1;yfbEG_*U)(^?1xkX(!n9F%=Hc8LpALV&uvq|Q1 z9(CQIX>PqT0+4MdsOxo>%Vf99cd6SY%Vkp6>$L4e0P{S-Qt5#Cyw6VOeV|u$I%GcY zvs5~u=Lyg-(oCi9X`25AdX;ICsnk6UBOUts^70L6J3B)(8bzbg z2u4EF0LR%B2!{b#1^`S>P9mGlVq;?izVAZ_G2#b<0Te~S%*+g?r>F7s^c28k83>0V z;JQG+kH3i|5{U#H$AMuO==FM7US19^`+dN5ArOyWqtzNIoSU1&)YR07R}=-hu48?D z9mQf1s;VNLP6rQKEg&Ahmbka~1F_g=9325d0015y9uSE{5RFFP_+GDvcDs%F`FTVl z5y1C>{e9r^@no!XHs%c&>u!!QHU3kcjUj1&0jpd__|O0il>h($07*qoM6N<$g65|r A1ONa4 literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-111.png b/web/www/routino/icons/limit-111.png new file mode 100644 index 0000000000000000000000000000000000000000..3c0051deb1e7b6d2a682bdbbf140a60b33907ebc GIT binary patch literal 545 zcmV++0^a?JP)WY0e273`a8~1h$rA*o3lW8EqM3XDEP_)kO=IcwgS+^-oR16Vo&%Ct zJ7Jih>zcIfE7t3GtW?OI9kNm(>-9U@_7z>%WEduXt<^{(A_z*jxH!f2^#@p18Ha~} zVE|iOz~dv}`@qc&FdPOrI{J>Yvo9!@e?;|>+4J^kSuUGR$3RDr$BQTkO0=vYI~{U9=SIxuWT!(~){sF^dXY&f*lwS*)l!_zHWRIw&B#_uvE4qW zlmc|!1DfVNJ@4;CD?E=h&3n4;LE_WX(`W4NzK3lCng#%H90yWL0U* zBtXL$v)?C4E|*IV1_M^BRrY#4l6*CHyWO9!>-R~+7z<3NdoYZsvzn$s*L9GDQtH*b zhG78H>7GC`38d2ifK)1lMx%jRtp=qOgb;ts5$QCLOhOKmr?!h&b%Sbd(00000NkvXXu0mjfk#+ds literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-112.png b/web/www/routino/icons/limit-112.png new file mode 100644 index 0000000000000000000000000000000000000000..2f275ca3afc45c8dddc8e76189bededb3a963bb7 GIT binary patch literal 668 zcmV;N0%QG&P)3HNdty% zvJs)i>`Rv}EaKL7Ws%@l2xx?m5n&RDegGAw3mFn{W6ZocT?|Q^QmBRWz*`*Ny+0nj zdoD;~=(w&%(@Zg+KV~9v%Xplenv6GL?FuX|8D>d%aJXpMQ&d9#9kjK&@7TloC@@Q$v1o zauV0q*Qit~a9tO%SPaU{4A5%5L9M2NhS6fVOp<(gdEwdF85b57cye+=k^_HwddiE7 z3nr6EW-=KHA$WCl<-e|6CJm#d!f{q$7=xcx6a~7jgCso98}cNvxw(m>qa*0Lj=Q@% zecIDUJ#1`jU}(@?EevA4H})zwuL3I$9|O!$}GF5tRQ5sBQO*&J$|o}T_$_=~^4 zzlY~}s8lK_6bh(TtA0VV2}B|{3b(etK-b^n@DNZ{0KntpBSN7N!r}0cmr}xU9P}d? zjYgq|!@$lC@bvU`q;odn1dKGBBitJQX`hW?)BXSoK~0D5=%2#?0000*lO;@)5JfT93TPe7{$oSgxUhKFMD0Gpd%kxu{U*Z0YelcQnSER{kwn`9XBj|xMw*(6J) zkcMH?adNMW802{=8b*WFD%tDtP3rZ?YLzsM20bqo!@^KxrLs-SirDSG4fLvRm$a;i zmC825Py}tezDXO%%h_t&d<+te}A7O`~L9okgn^} zG)=Cotguih@cjHd?ygoNb=?)w=`2Fm`#&oP0#sE6NkmaJ;1xxIWmzZ|i%2Guczk?} zM?%+uPG?bsECY!I0AOTf1k20ISXfv<6h#n4vG0Q*z|71HmX?;#ZntrFcL!OPV^|^q z$TCC(0pR=inL=gaRb8~~y(NQFm$>03L!vk(_ zZ=q=#R8<9{2-w&F?(e@3bz0YyyAy>ExqfJLZ<^o4JD`M&eO;e6*s z5-(1p(ILl)D4G1GaQIB2kTfQ%X}baEoa znSA?o+_2Mfu2>V zNQs1}_4Slo*F=spuSn!Z+3df8o|Vl?k;si4XTIa})6*Bm$KR7of?)uV$z*U{m%hHf zSN-|!q;__8B&p@Mx3?uprBX?Y zi;IfIVsf8{w7V-=R^Fssp24zOlQj&3U@(XzJkM)+fO@@7DwX2)_7=b2kMX+`EDOrz z855rmy1D@9?Cc~Ki!nVtjpuoorrGiUdwYBA@9#4)F~QZ<6&Dv5O;}eK_?OR-p_SS)gVecimQ)u7S9$H2fTx!ep>Q?0`O{(gFTdS3WIAV4%4B^r&A zPNz9IIAD2sx%nWMgMoolLn|xa2?js1wFRaLK&ezB5D3uQ+xx1&zrW|<;eo-yLGV0S vU4^^5A8q|-ZQg*k?q(ZP<3HtD8xK~y-)wUj?d8(|p6pSuet5wD<8K_&hJ#X*j9h;WOIgSZNAX~3ha zgVd0{baZHO)^;*OaCH|0A&0{y5FK=I30wyWIXMTT_ubRs@W5KAh4g`Mc=*2e#|z*4 zJ|Kw~$M+RlR)(f|NKLzBI!(^Zkm)q3X_qw3Lt0jbzOVdVE0IJb#~H)dub*&w`T>S9 zj+GTa*MUR=cz6IB4WLp1YBdL|t0nC2e#7|q&!|2k+x8L-!(uKcSg(^p@Q)ILtk=n0 zPS7wc+V;{jBLq3l7!9MwVv%%RzDcf27K@}|)aW>4AxsFFg~A^5`GB3y+d$9ibjW-@ zV4<)_A!N|9mYK=4Xxsk=dX{aInM{k8wXA%8disLd+4nF_K#~9e)oK+&2n-GmzVL}e z0w*UYI6ptf_4PF-CMJ-Wo(7uDcc@ksP}iF*mr0V3kB>Y)KIYuq9FLBUNHX%JQi(S= zH`FwZ8yg$k-ri=b)e5^Smq}f3%4oM2q3hAlN|FRsRY4L#5JX;nXlgd?HrK)bytBNhYVaR5MHUmrF%H?gp=fFKAU%W~v}5KvVW>+9>tX0tdrIKbK2 zSqO{AfmjSOd>`m`@i&o#=Xsc%oW$DN8it35;dx$o+3ffgk{Ob^y=&(bGBW@dosCH+z^G|0&OUkhH%5FU&|#L9c`?00000NkvXX Hu0mjf=0Y#H literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-116.png b/web/www/routino/icons/limit-116.png new file mode 100644 index 0000000000000000000000000000000000000000..ca9fae9d7b7c78a089f8fbd1ff66518b3df031d5 GIT binary patch literal 668 zcmV;N0%QG&P)3vk4>$7FOb3p&<(*m}F3=@6T9;BnGmq7!D3Tqf+?@ zU7toK185pBItn~L1C0i7eh$=X7FJhF*xUPt>FFPX_yO58m#FIovsuY{os^P)m6T+? zPG+-`x^B=kmtGhi$g*P8^%{#s(zf|7*)~}$lDb}_WyL(0l!ArAK65#jozD9}FY0v2 zT+U^ouumxkXc)^(r*CPR{|0)IX_D#mEe&JY_od(ein+ND$man?0RU91RY)lj4u{|P ztE(%Vot>fI?;{qAL7AEYn#~ZZRUfEnO_s|f$zHF=)6-MV&(HJZ`uZAyKmdtE z0^{T3UP)*g&~7gZ`2B#Y0suxvMzFcLiG_s)xULH!#4F$H^$-q+kxHddsZ`KvwLF-r z0)9UP90%xj@g9XjAxun6U~O#;kw^r#ZF?KtF5oy2h(=pzG+qToB9Y%u_~lhqMJAKM z%*+gSc6N|RB(Sow;vHx-fM~R(aC`e0!Qdwx9s)uD0PgSaF*Y`aKp^nOKRrF+;o$+3 zlapRT*9CTWfyc-1L#?wRcfe44bBI&pKh3itOxmBVgF}ZsVq7r*0000^Dm4J$aP0(S!L#OWUIw@snsHLInuJqbls5-ObC_P>=x5$$!7C?pcgfpWI8RG z&2CW$6}0UIn&u51=ifjtavah$Z)n>KfiKU`UokQ95t$62C;)&`sRSVeB9VyirIfh8 zzellHM6Fgsyi)t3M9#=rzajB9&&PWlKFg|Bz=EyaKQ8P zbEZ-$uB@zZY;252M@OC46$+$bTm;c*%)v1HpH&nEnx=syq?EoFLclalZ9K`VO@L&G+_7>rA7@DSaKvDu58^FWE_nyvK nk2j#FyV=9m_)mS-gQoojlw?3}tOGZ@00000NkvXXu0mjf0%0D< literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-118.png b/web/www/routino/icons/limit-118.png new file mode 100644 index 0000000000000000000000000000000000000000..3739a4ef0e086c2bbc31896137b3e69068767b02 GIT binary patch literal 671 zcmV;Q0$}}#P)Lz-eg<)q1`Jtb zD>Y z$mIY<0j8&c=VzeP0qS+2*)*`V^&R{BU$D6NV^lvP>w1Q&sxhAzY_~}v_(usrw%cSr zFQ}>-bv^USa6yIZttt&*0-H_5WdYL!&gCJiI*!i11nsT{IUaM}zn7Q2zSyZaT9ND0TsfGh(59v&VL2m}xghkx^yWx=v6 z%+Jpw6bb>31MKYqkB{FbI%gB!fQjzr1XJTb<=F(1_9uQUPJ8Llxl{lE002ovPDHLk FV1jsKCUpP+ literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-119.png b/web/www/routino/icons/limit-119.png new file mode 100644 index 0000000000000000000000000000000000000000..fe6e3f4044e5fcf2f733fb2cdcec1e95bb79cc48 GIT binary patch literal 673 zcmV;S0$%-zP)xK~y-)wUoU}BT*E^&rC*>h*>`dBMLzj1q%t2VuWe3SXo$!T{d9Y z#!}Xhe%aE(#a7&{Tp{>3h$4iH%}XR|ClnEjCnI>i@|~HZs9`qz$*^#-5(d; zy$2-GbEJ&WwoMiahfJq$m`ahevt%korqeep6b@%I;2D%U0R zd7s7NE0*{W4NHX-t$H%<7 zx?(n)<@)+MS65egcXt9=8_6c1ENs?z~JB@Ha0e}xVQ-4_o1q4=%tj%WHNYqdcxM$7K9M! zcDn&A8U-{B8l(g|9lS-Vs$zbA9?4`9_xJahnVAVLIvqetsL=Im)ayM(K3K2wFFa}yfQiyaeU2_u8xri$? z>_rb!*r@li7omri;y;6 z`&>TX_eBz4J+s?}Kg8$7Ll4wz{$9eMP zH(tK{165*368r{e8*z z^-X+V+TWLEW_($et!g!M?YI)v>v37uj+U3Diwk`h7Z=jh29K$dGsMTt0Zf?@i(ZP+|x1m=1iAp7i(&?IBywE?A zN~MxUMn<&0zOJpUEoCwp<@0%2mZja@T}jg6;i0CcrZhP@sa!6nqYodXQb|guYXKbR z4w(${JPgAi5{a}fJUugpwzfuZZ!f#AU&GzI;5c^zm?m^~ z(rKFH^LdiVB(Cc+F)_jD=qL*d3nUT=a=9D;uImyGhlxg`^z`&_d3gz;5SS(bJP$s9 z=1YRXAluv9EG;cDHa5oG+#IH9a&&Y=EEdCY9BkXB(P%I{Jbab+JOcFfz2)GbC5pvj zba!`ia&khm*`!=9Ge1Ai#>NIKD=Q=t36jYq#bS|ED#iG{d+_EB^!2?p^x(lCL?XZP z>=^_CoSvT2)zyXX`#6rnb=_jI7>!1Qfq?;B*X8W&jQ;+9y24?2_z*sR{Ijk9tj!H* kYd71t8b4^y+OV|$0-QHuf)1U00000007*qoM6N<$f@C0T9{>OV literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-12.1.png b/web/www/routino/icons/limit-12.1.png new file mode 100644 index 0000000000000000000000000000000000000000..77fadc6818db3d17b871b64d7820fc826c06ca58 GIT binary patch literal 688 zcmV;h0#E&kP)&x0 z2h$wJ+#I0mKyNSb^aNC^z{v?vF4vGue#hF{7Yq;o2Ib(YF81sDo{jiNqZ(YgYN#YJI}!=v!p7fFz+*DnST=!NEbC zot>drEW-0VM59qik`(m1qkA(o1)QJ1MyaHLx_-ezfiB7Y{e6y&jd5>pk4HyGGz^2O zREnl)^7QnSB34Jix(+zbv92HjB+buDp0}Y^D|-erCZXBc$C{jsb$Ix9 zpmQA_N|TeZW@jJEm;?%fTPhYmDh&S(bgnRzip7r#f?LVOZub#4Z{DU_1(}}&X=P|8wzJJ*dL+Q;MsZe;IpxqwEbx{;ySr(4tT)go3_&7^TOE`|h z-rgPnaU5Uvn@wtPh;CQ&1REYt#-*zr_*QoD8go7 zV3(bpVe)wZGMNnNbejGBed0LA_kDcdXKHGSMx#M4mjl4}eQLED3yX`~`TQ9M26ipo zyY~XexzDp_kVtTFa6n&QAI2Ek?KbCi%VaW~o}Q9QrRa1z#BmJ3^=sE~78an>dD+u{ n*5d~Bw3|I#jsLV~Jy_Zw4!m$w^3fMS00000NkvXXu0mjfq)JIo literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-12.3.png b/web/www/routino/icons/limit-12.3.png new file mode 100644 index 0000000000000000000000000000000000000000..395f1c55d5c0dad7bec58eaca7e4928bac3df8f0 GIT binary patch literal 760 zcmV1U4$Mwiq~$pA;o(3V4zyae609HXnGbcW(SQ5rI?uK$-9Tnu#Fu$$X@V0 zzF(Km_x(l^e|xU$mu<(C&A!n1_xvu}8Swj*HN~JK* zp8de<*WY1TeIybPiNKvZP^rM-A-s75ySpV)so!|=)yMl}ce*)~=S8q^m1^6<1f%(voCZyDF8!Uu2%=)55}2 zWioYLUVfeEmdi^ilc{TA;i){&hip5pSnPuw=f8<=aU3ZY`yktn`+uy}e&WG{Z^>rC zFv#cgc%DahcQ^a{`{Z&tT-PNW4r3Sw^?IFyg9EmgX?uHH(P&iZbXt~WX>V^&l2j-Zlt?5rF)^WdJg(p0zLj3Rk|L3J zKFZ~XM5D-cF${x1Aket*Cx}L&Tz=@o zG@-SXR?{S%PBS_>O08BSnM^V=GQ#})Jj27oq*5sW1_uX8CX*D4MJ_HbF#pVBn)q;C zs8;zae!ricogEez7a1EHV|I2H(=<6cI%0EkljY@QW@cvS>gwY3^b~Gxz;*Ev3Kcm# zY>0xvARQeYoSd9cuh+@ta?H)mv9Yni%E}4@0|T_Qv@ktAO>b{6iDVKS2STBup+}E? zArSbU=g-0C&!b!}b6dAyFv#`wH9b8&R4NtD&(8_nyGP*eU3mN$K7RbQ qssF6W4QOgNo46YP)1Eb9X+HzJJ!Detlazq~0000hlW literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-12.4.png b/web/www/routino/icons/limit-12.4.png new file mode 100644 index 0000000000000000000000000000000000000000..4ed3146bb24346e815f6e2327710b0ea4cc2b01f GIT binary patch literal 749 zcmVpzOAC~K;mCL=>^z?hh#+_j2try~ zDrj!5t@HD*1KsKTT$-C}YiX&VAP6DXeXMl)qiVJP2D($NCZ*FK<+_i<&s(jZ85{eS zTn-F_YPCub1Pl)kv$wZLsZ_%EePXc~hGBHy`tb0OqoX6@=`_Z{!8cT^VPshc+St&o zq|MDuB@zj(t*xn2sVJ37$#ERnwyoXW-R?1-=P8rP$Z;G6lC-fQS=K>_X7drL6!Lux z!ypogbbokeW`@<(RU(lHr>Ca?TwPsJC=@t5JEO0!4+A>(X*M5)Fiq(1r{6Sj9EZut zNm{KInM{U>i3t`K7Dy(OWV2ZSip3(uVv+IjaZXN7_~XNejt@7R`T2QF)8zR0n0C93Wmz;D4H}I`=i=L2@O{G1$Hu-Rk%;o}Apn(1g=8{` z=XsRNWjxPgYio;vfdSUn*U9JeQV$00000NkvXXu0mjfOwncI literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-12.5.png b/web/www/routino/icons/limit-12.5.png new file mode 100644 index 0000000000000000000000000000000000000000..cc0ebf4bbf413d71e1e74fe5876b36d161d1d371 GIT binary patch literal 761 zcmVb2<)nLL4QClGX0bWD} zU%Du$gHrDjbg)Aa9<6R&Ea~ELP=>=7rRkwALlg}bGF~@uYO(j-w}Ut;Iuxu2e#g&q z`Tjm1M1)_S>#AbgF|n-YVi=#LwN>Q$b&=Lq5ySW_mi1h0J0`BH{+?AtgetjQlqXN_ z^X}aUrrFHx+tAYkem{Ks20J_O;R9@JQ2eoJg@Ke?_qy%xpCtT zmIaDJI-SPzJQ^Ar*xK46l}h2dF41TdMNv3AJ7aZqmCemfve_&xH*ezK-MvaWt*Y~i z@`#a3ou)B3I7mlF2jy~^csx#fdplE8Q|P)*B9Qzydlx=_gvQ2C zij0lDAQZaCvuEJ*k7K)MIw<1^?pAjlW=nK rvZnv6#to=xH*2^W|7p)^u(Uq_lp;^m|Lhf300000NkvXXu0mjfIQeBf literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-12.6.png b/web/www/routino/icons/limit-12.6.png new file mode 100644 index 0000000000000000000000000000000000000000..1e1ba981965a4195a1817b042dfca4b276dc7423 GIT binary patch literal 759 zcmVX#6div zp-UX3a8>uCgF}ZN#kF_aAtgAw1fqrD8>CS%6&wnQh1?K=DIIL{zHh!ah~e(Ip$F*) ze&h2Qe$Vs$B8h)I$MMRt;!367XlCYvMn|Q4_oUHLX=dhwQmHqxthgM<`|nvp5^bv0 z5U*bSOfL5mra8i+M-Yv|ojdULE1aIfyLWJOR3(x4gXhnGWn|>f_I%r1EXHJ-mR43= zot#LntN+AxrIQnBWyO_gS}GP}w~iZ8wHlIX9%*Y!YBcm+G#b*@mSmbos#ZfcGS~HJ zW8VwrvIm2AG|lC6~)lIy(cuAACMM zI1YUK#^3RJy&N7Mvbnj*^z=08bQ+(}N4Z?4yStmdzCPyX=LrUbG(LZZW)mC-4}*gx zPEXsSU@%BePY;)um$X_fve_(aYisQ7?XkVR&D7Ks;c%FAI!z=JVJ?w?_wQkFuw>}T zlivsgp7Q!Ncs$fbFc{?e`kLY4VVcb*wOWm#p`n{Ud-e>zeEGej pch=zqbTpeCY>gk(XB}wTzW`JjVi@XvF@gX9002ovPDHLkV1lwTWAXq1 literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-12.7.png b/web/www/routino/icons/limit-12.7.png new file mode 100644 index 0000000000000000000000000000000000000000..285c24e8df1262f87a89e9d6d10d01452b0c62d8 GIT binary patch literal 755 zcmVKW`GVn(+>o;30xS1^<9GB!?8wHoE= z(;s>DY7)ok;=u!m$KlQ$`1ldZWqAD>wzjII)4%ih@h^0B{n^Yn&81R8j^k=#!f1C_ zGDe@o7-@G`nwT(h99N}M;?{8^s#c?NoGmRbN|%@VEG{pl#YM?+wp6V~Z)C>!G(Z1D zQ&SCHTzsDBmWvB%YO106`6n{Qhg|o*lF4@}mA*`LOQ|F!lkeoZ_x(TB>p#=m`#sq# zSQf=%5n~J;9UbiK?2yal@H~%bG>TK z7ccaeq_wp*_4V~>b#+zwd|rt}LYYiPj^k*1ds~uJEEY96IjPanQT6on=#P9}dihd{ z$KUv<)$WlL+xx(QrggXiHR5_wCx z+!TewVM3u0CnqN~8Vz!}9J8~ttgo-Lw6w(O=_&jB`=nAS+S=M~Lgg|7BK> l0WIxj3s>WR+Orlc?cbGNW1G|@RImU5002ovPDHLkV1j^Xakc;e literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-12.8.png b/web/www/routino/icons/limit-12.8.png new file mode 100644 index 0000000000000000000000000000000000000000..21d392f6b935acc39723be4f7fe197856a412023 GIT binary patch literal 759 zcmVQ0r)*NGmeRHUh?JI&9(mN7o$IM0<#ewOS0H_-#GDCx)ysx~$@lt?6$$z){Pwsv-QBuTkkPJ@Gk8Xq6m$jFFxTvz(=L5jye`Y4y5 zkw_rV!?G;G;c)%J6B84xtgH|YhdDhx1)y53($v&MB9R~z3h~F))&2XF%g=lS0?^z{ zb09z_lcB%ApIWU(DwU$IuaCL8IeL40NvG2QoS&c5+SM7X)Rp|i7->+5SOl?ojl9khf(@bV>GUH#h7 pKWlIT8k)@pw#NU|XANlDKLH-@Y6)BGJ(mCg002ovPDHLkV1kffZ+rj% literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-12.9.png b/web/www/routino/icons/limit-12.9.png new file mode 100644 index 0000000000000000000000000000000000000000..ef15df9aaa30b69e7333c9b6a2bca5c600e37016 GIT binary patch literal 754 zcmV-WDmre-LRbgp4SoVC!v#SjZkEMCl>zW@g?!h@rdep$pju zzT@{?zVCgJ#NWpA!g8FHa=BNUo_?>95$XPYX=Fs2o_?=f?v)%TCC>~0bJvhWn_4Z- z^XEVD=FJb-_Au!*B$LqH4WB;2$qBrD3zbTZOy)P9JbA(J@E`5{w%K(p*|wvlC11zK zlJDzZ@qOv|SXx^0W!sKi*SdAwh-$UCY`db34e9DiU&Ymxw6P)Cc15*X{6^;cA+4=F z)ADklM&s*5w=^2k@^YZHwP*7E5OSOurBWZ{y8lgdi|b0M)CW1vO!((k>nRT&d`~V1 zhC#Vp#`k^t`};XOJfv7G;&~qNcpSqp2!a6Db=lk7qpPcnZzm_=^z<9b z{*+WG6f`zArk$M~l}aU9mZfYqE8DhpaBv_=IyyR1I-StlO+8`Cs7s8rzLL#Wp$LzpH+ zB1BA+Y&Ofp#00HYi`m&(#>dB5U0o%SNRY{70I)2J>+5Us`8=-c(rUHf&K)pKLU+zz!(sOK_gPE-DPug6M!HHD3wag&(D*ZnE}^@fq{319zFVnX!J*3 zzJySS%gal8dV27EpL)H{ZQNq97|mvr!NEbA%_faTgTcFZ>5fF;@niV-@z;+2S%(wQ k(QJ0GHU6hQ>p;`~1#(SjIS$e+i~s-t07*qoM6N<$f_S)nOaK4? literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-12.png b/web/www/routino/icons/limit-12.png new file mode 100644 index 0000000000000000000000000000000000000000..47e9b77d2029b87eb37527ec26ac15824bad2281 GIT binary patch literal 645 zcmV;00($+4P)V8GsQyTl!?SG<8g9niHyg|MBah5^jZ0?*GtrvqGF0nMh3Oy&m;4!&Y>@#p0H#B5nf3SlyrlWez1Dfuc=lI=E` z%Sj4h(z23c$4F$`F$&RSxlH!^d=vdXSuT@8G-=zhkxWXJrP2}ed5^u`+eBk}Ju;v7 zSSlS+N)V5>lhA)NT<_STU*24-X2z0SCPqN{IXyW2n3+Qb%DVEe?!wW)a!K|9v)(I za}&j45us4X9~uk**M*8`^ah>ItD;CGf>0=g`}=!%o`+hkhGMaZdcE!sb~->bdZTc6 z_ZxKmBTh~LRrO^aA0II{HwP&t9LK@9_2%d2q3il6%L00000NkvXXu0mjf@H8R~ literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-120.png b/web/www/routino/icons/limit-120.png new file mode 100644 index 0000000000000000000000000000000000000000..16cd8368abface047859839aefa148012bca4ffc GIT binary patch literal 735 zcmV<50wDc~P)rWB2?nZ#;1MAPOlQGDI+&nP(3+f+t3xo*(xHX)f_ro##SuvJ#5NKdVsqGtiw11t}i?EXzuGUe)XW@bKXi(rIwH0LbU_ zIF3Uo6yo^!m|QN0ZQF#yVM?VEdwYBMd_D#T2jTvGI6b{bKJP(>ajNZYNm8v=)9&uB z#>dCCxw)zR{e8t^F{M%|&CJYbYimpM^YfaUn^Q8G)Y;it^LN|Zl3|>>sa79h7_H5^ zTrPsaU~9_@3k$5TuhZMx%g)XYe!riMjSYHwdN?{dYMz8)K(+eFjn@mEod9%nbdXA= zn3$NLUavDdJ4+-IVR?C(NF>7a^fW%7k5DK?cXv0}*Vj#0XD4{QxUp@xz2#?lJRS}X z4p?1XWol}QOeVwF*cg>c1S_7-d#H~sx3ip3v5eSLj&b#-xmeomv& zAeYOL$z-V2YD`W}lFep`Mx!JWiKd}gg#P}LOV6JDM=!nhuFfcGcAP|5?175sz%bZ0d37@8(ZTq^;sL5_B+fyROgg+ R4OIXD002ovPDHLkV1h|fVbK5p literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-121.png b/web/www/routino/icons/limit-121.png new file mode 100644 index 0000000000000000000000000000000000000000..4193181f2f23df7d915084923679761c1b80fa8b GIT binary patch literal 678 zcmV;X0$KfuP)*c*HvhmY3B1sOeSv_i;>gQWGqG|lQ+!gk7$}{x~}rNRw4->+m2#?{|nB~KS9^0 zu)GXt8ZbNzJUs!825@-^)M_?XSASt==R2mRe*5*l*|Jj9b(7hwV7*QX!8Z|ttk=nG zR#4YXT2`v(coErll)7GHsYJHhd>8FDSt^mbUZZVCUt~haEEe~e%Xw_I-Y4qOYLU5| z$6|4hLdc+LE;60Ir)B+{sE1{d>GVBKb5Z%)?S8||%tz$&fFuC`48wpB0;8j&xVpMR zrBZ?Gx`;-jkR-{^$HvBh$w{Eu`~bsHKuv41Tqa3AKR@&I^px>o)ze&=4}23=)Y1y4@~TR#q@KH;1jQEzHi&Vr^{=>+9=C zBogpE@2@Nv1Ofrba9yC&!D|#nLA6@N?(QxY78Xz_6c7r9@FtxO;JT0ziCm-6=*z~( z#}NvJaCdhH&+|~JR8S}sP_0(`a-T*6h(xX>Zf^d7s(!}70U*l&fQN?%jEsyx2m!}& z&|A5Qi3zBx>gVBb81Ot`dmDIs{5jA$8}J4UbT4VMHVr+mTS{Li+nf1_ng>`$a;b3yH;c#4sXaS&p|}MMP+-Qt`31^_ioi zIdr|3i3tdYp}ifRo}g5M<6}5EsSu6+WO?};y}iGh{wBFl7!zGLWOmk;Vo}7l<&Uy$ zkz!F~cGebMH>6M)dto#{m5NVv{X|w*MegtAuXKMevbrjw>nBpF_!=3?CtFl3WfO4*9VV}u)qIKrB(|N4mUTe zC<>aUHMcxHJxw~D#^do&E|-~^nIWA{qiGuDa=CF5;V{%{0TrhcTrL3G+S-_!nqqWx z6w@@BoSbB2WQ0T_!O+kUYinx^4-b<_B=GzFM5ECL%;f^76BWyX=V#u^;c$@6W?5NT zAs7s@xVT6@pC^?{5ex>2$K$x&?#AcmXRs_(y1UOQm0p3my1HL576}GL1_v)CncNXgi;7{`-)9vOp+T|eXJg|FM@N&W zYJgY_A`xh5ftMG^<>2H5PEU)(<3Cwi+9VM8)yOx@x;`YTs>#HJDd*=RrYV2KG)2zO zMJ6UpQB_TJedyIu7Zr3|R*J`z|^z?ipnFK`vAe+r% znkF3`9h{w=k;!B*3TrBY#jex6dP#Qy$1mSwTIxk;f=V0U+ya5zjb7=))M*xUPHqg)OVi8Lmw zC<-2rr!nR6@o~1dw{g4O+}zwSJv~i49>?u=^Zfi=Ux`Qr%H@y^hXY(L0Gv)IV`F0s z3=B}K)fgQerN6(Qxw$!ddwWSF5{!(Dkk99-R4O$h@#1g#`bu?wot(?XW!tW%rULElNkO2W zDhQ;#J!xtxkZrrl<>EC)6;v!nW!u|YSdcC*^h>(9kQNps+ul~O7_GvBAf$BqwWg;l zIzRt4P)+CO()4si>GW#_K?u3-BP9}_l*|1dsHR*_N+dqXbsvRamdmer@Zb+pDKHEG zve_&_5YW}t#qRDdnM?-X_lZWM7=}TiP~h?Cgx@=4OmCd1x3?1n0iNek zt6U@!!8A>-uCBPgz9tfhK&1lDp26qO|2FiWHMju{?Pdd4gFH;Q3zfUV!(EB5(}viVj@wfB;P$9LIN!vETj*7+xI@p zIqyM4`0Z6Hx>#0NOmjwR6oQr(`1unu88|wE}Y4Q*w4DqLlm< zr9{roL}D=|hG9uMJyLU2McHhR7{;+A5+cQ-Jc?pbB#{s?jAO}Wd#W;}9J08$D)G22 zg~H=RH3|ihc-)r7#Z^(tfml{V!r||dPXC*zMmjAL4u2QRis;Mb@;jbAdx2?!rU8&j zrBF)I-rmmX=_$!%5~UO#kB7azJx)$exVX5Wudk05zaP%eJ)}~)zN~5QnV$ACG(@>v zW_Nd&g@pwIfdDr*H_Xq^lgs7U-rmOTcC)gw!u9nvg+hTyB!cJZQ@Fc>FJGQG$mfR$ zh3b>lG!3`gU7zyQ)D#;V8+be(_V@R>zrRP)X+U^a043J%?4hL|8&n9aA|)5fgDHr T{O%UP00000NkvXXu0mjfFL7JT literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-126.png b/web/www/routino/icons/limit-126.png new file mode 100644 index 0000000000000000000000000000000000000000..794dc21070f24e56a4d3a9852e0b038546f033bf GIT binary patch literal 733 zcmV<30wVp1P)I%?W>g$U{nM+62u+nr8hd;1 z$Yd5Vj3HjVf^Zl*JK^U~$mikPH#j>hF*Ea(t*ws?4SjF=o8(;Xr5J`O3k$AXUW&M` z{8g?ia(OATu;7Yen3BuAd}K60rIIFwaVE*6NVO_YQnf0QOo|xBnUqRe1LnFONhEe< zX{j!i%F{rPs#HXlmg$We$R^+uZhP& zQ2@whv$(EHPfrgQ7Z;?{X&lEv(=@KHuQ@q6!M1HQO+y(NfI`7fHmj;Tit>TQ#eT-e zv2B~<<6|~9HW(cp<^KMj_4Rcu%OaIZ5eNi0I5?nCC~$s$P9zfHc`yhM50FYd^H462 z6Am{wt0)RSpRc*)`T2Pc4-W|h0vsJ3adUG+cXv0suH*H38z&JCL%BTep{)%%Isj;I zZ)a|9juo z%IWDT+uPerOiZw{vclltAb!7}+1Xiw!64OYweh%C1INKbZ*P%&{x?uC7{u%Ka(jDA zyZ7AK&k*=%8J{HH!^LDT*M5Dq=p)~mHL P00000NkvXXu0mjf1>{OQ literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-127.png b/web/www/routino/icons/limit-127.png new file mode 100644 index 0000000000000000000000000000000000000000..f99d6e7a1b51a3e75f3ebc6d15d5a9b273939855 GIT binary patch literal 725 zcmV;`0xJE9P)imMMIY? z{?Mws6uLNA#MKrY3Jw7W*DDes=7NxfUmr&Kmu$BEQD&MpSzX|A8MFAid zi(#53EiEmaot>fUI+kToEEYLDJS3e?lgVUgYHH$rUmv8??})`zbwg3UGCSMGzyRfP znS+A^mY0|DdcEA-+_1E?L@t*j5{aOyDyOHX7>2?A{yu>~0FTE5_xBKq)Z57C2k`l- zlT{Q2m&;Y1^7QmHJ3Bi#olZJCJK5XYV`^%OU@*wo*x2Jr_dC$>FHr{aS^ZA%jD!FmzS4BqfzGP=du6EVYfqF9X2WzD3y4Ps;Wez zQP$Sh7#tiV91hdm+>Ea4xZQ60`}?0imr78nV8iV`CzX03YHe-B>2z{+b%kYF=(MF0Mn^}fudja&q*CB^pDVJk@Ew=yBiq|xvjLFH<#0G0n5IcSpXaG^ZEbB7 zi$xp`2QHWEQDj-LvI2K^KWe&XHEuvnyII4l@t^Kl4KD3(&b&VATA3QD00000NkvXX Hu0mjfjfGPy literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-128.png b/web/www/routino/icons/limit-128.png new file mode 100644 index 0000000000000000000000000000000000000000..89f15229d6a00bd2c61277665d3bd1a8aa7a34df GIT binary patch literal 732 zcmV<20wev2P)v3G)@jegmgI06Iw*cz4vqo23k6_kbdCZ-tT95 zp682*@XPbOpjcK;3WZ%sr%z>QNMv|eWN1hvoj#R9VOK0GC!QDlJ*$ZbZ5oX@Z{NP) z^XFL%V}N`fG8wpkA8u}-T7@rP;P9})vu7W9`SKkD1K-;Dwz*QtieXqXGo$3>L_{h1 zBT9*!oQTZKC@~C6DwXV=qa|uI;$j$wvc4{IbtQkr)s@Klx`<&MN}~~P$&?Dn>gpSr zoAbqW|4wv=>x#_H`LeqDMwAL5mi0t(xeKXO{!Mg8r6Q8cU5I5p32ro-uNWD5N}&Ln z20*!7Mkz&4PY=h($COGXJkKK*i(#53rfH&-;!!+~_V6Km{rZ4%IT+l~wAake_A@a- zv)Sa};DF`jWkyFwxxBn&X=w@Dw%Om`$F^;@wzhCxm%Y6`(y0`W2M6KD5BT)yUI53L zAd_iNR?{@1(P(?h)6>)J?CcPYMmak>BNPge&1Q*2B3xfzw^kyP0mqpL(A5Rq-2j9_ zA*QCLNG6jsn@uJsCm9&*7Lsu68JP&Sf`JG@e zh-sRvt*wztr6?AQ)M_=-=`@?0o9MdE`T2S4^X)Bo9s&CLYE-MghveqJ zr&KDju&_WPk)U3$Q!Ex4A0H>5&$j~AD)jZ$G?|}&Pc-_R?QIAI0I+SFa5#)o3deD{ zJGuV;eth4@bzNey7`m>5@59OpeEvw O00005P)V7i zPxL~eATl#kl2~kAY}P)zua8cpS^J@caEFlSz(`kEyAtp}Dyk0s*+bZX}U#I@c8C8`IN%`uj0WlY@f; z78e%@27~1Dc@`EH$Y!(b@9$%pCYzg^+}_@DczDS0@Gu{IK6rY9y}cR-xm-WtaAmTJ zqTum(DpQ`EoMd}@o5sdQj*gD-dcExI?BH^_xV*e9uS7Tuxm>>kmka9Z0I034WnyB2 zzP>(8(`0mXl-}N6=I7_>>FHs4d6|4ZPc$0EFbqu7EX(TZz~#b$WkIpXTbxcOXJ==u ztgJ9FFhDdK#pChN-Q7(v7^J18g^rGn^5xgS1#98yZzrpceLA5F4vW9=lws8MnCiL;dhkFU>GzS z4Lr{y5{b}iwW!zY42MJF@i>NI0N^+d^?IF&i3z?@c1$G`;UAC0x&trWFWxW z+8W7Z64!M}BoZtyFVpFCxVX3=5C{M;8jUbblSm}O^z<}0H#ZOrLLh(-*M;GbzvB1% z+27x1cXyYqtu1C|W;i)Hq1){ejYjGB``EV4U@#z&NZjUK7ay~;9gdF1qG&XVX_{oS zS^R!Ko12@&Vlh&w6nlGnEG;dOOeU#RDx}kC@=u;Xs|B;O9Yar_{z^Ff1212K&&S!> z8BSr2x9`X(8+wfU4#s^?ZQI(z};Nl^KiNM z+yjz$wR~TsuIE^(95Iu*V>(SPE|TdqnaSL-QaPfo=ji+5@3jI+L|9f5hlihWdHD&d znnIxfC<-9Uz{?BJ=>S((K&xe;Sp1IN-ET;xenj<=*)XzHRh^|$z;>Gq0{)dCAlq%S zR0^o7It?Q`c8o-pm87b+Sg(_V0pG-6K-TM|sH#avp zKR<`-x=5u`5CkE*>v8VG>MC%1`yS1v2r5dKjRuD#&(6-cyu8fQ(^Gc4T^0%jZf$Kb zpU?C9`kEv;91f%0$;rv+bd3h7DBT!rdmY&<==%@^0W&i*APHHPp=lb5#UiGsr}6y! z9KHTH7ojMCZLh~5NkBY~xFlh7a}z5oEAV|E3kwU_*w{d?*Td7(6C_Cj0L0_*C>Ms| zNcQd>AW4Y9_W{?%uZW_Ei;D~F?d@S_X9u&hvoK8)rfFh09O4gL7w~<=kVy2<=|ubU z^Yf5p8QE+Wq9|f}dmG7Q5^HN~*x%oWZQD_9YHDg!)ad|;L=P;Nk6Eoo9sl_FNXKzV zl8)ohG)+2=L(lX0@bEy-^Iqq|kgQh8a`||oeKz4AFwws`!Kv||=Gg=$?N29Bdq{h- R29W>&002ovPDHLkV1hCMJG%e? literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-13.2.png b/web/www/routino/icons/limit-13.2.png new file mode 100644 index 0000000000000000000000000000000000000000..e3e32355ff6f19c151f663697badd9de61eb428e GIT binary patch literal 753 zcmV%prlfq{-h91hIh) zMajiWSG}!J>TM5O7O&ghDlG1Qkl@l?qABP^HraD1ffW1=3U0bHd3zA0Ej?@@ec(I3 z&*^(#B=NWNeN(ksO2y(!&CPw#*qHR-fiyNI&CPvKvG`K8T1vie{xfSx;+{??&hzKr z^5)IgxNd?>29imzZTR>Rj*j5%TiD<4kjwqdlP5orNc?`Ezc+iHBiF5IaWPQ6E(L-9 zl^~Gnb!l-ikn7gudCr~VR@CXl<+}S?UzaW~^;ukAO6%*A>+Y-5iQmeCAf(mRXDSqW zy14i}(H$2TQlZe(>gqEEK?v1qX{AzadES2$-QjssD%Do4mNvip^yzyZKKzPe5e$QB zwMq~K3=a=;cz8&;T&COYl1L;l41*vDI5;?*`m}lU~1;lF3Ghv$IDy z4)T2r!yp=sA_?2JDHICiaycTA2sbx30Q7o278VxR+S(!-jq+Qo1&#w}XOBWy7KFou zEsK0U&-C;(zV9`Y_Pn%%+SyftyYV6yG^-VW^HYa+1Xi2r4oaKgSUC#C&cJzlcS@1(a6XM zwr%4$4yI|cw6sJ#9%p7|hRw}Q>h(GRl}d$Dsl+eu-og9#Fgn^a^!V|QM5EvE;su04 zoSd96Ffc$Q65;y#n$y!$VzC&uZF78lOgJ3o>gtMKuLr=Fu^7>(PvPq7r@sDKpBvEE jZuW6C{--_b!_xi literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-13.3.png b/web/www/routino/icons/limit-13.3.png new file mode 100644 index 0000000000000000000000000000000000000000..51d365e429eeb0261db4883f5fd86329908e51f9 GIT binary patch literal 713 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 literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-13.8.png b/web/www/routino/icons/limit-13.8.png new file mode 100644 index 0000000000000000000000000000000000000000..ebd50598dc8cb7bedf38b6f4a3f0301f53219cc3 GIT binary patch literal 762 zcmV7{bH z&uX>4PIOPJCFOFTwY2n7j^je*azgR=2U*sC6WwE3Qat`a<#NLPO7aU2ST0>xqx$8q>362YiednuLNC>pKk?OWYS z+Su68`1rWi*Vk34RFq1kl+9+9NF=noyDLfB+S*b$99A}))%5hVezz>?-8(57t+;45 zpAw59+r}^q0)YUMFin$OE=MMl!SDBTb8`bgyWOU%tBY7HhR^5Y&&$iZ_%xeOU3fgu z-A%X0!|d!V6B85Iw$0es80mDHYPHJw`8ghs2Y}<_V|sgg+27x%*=*wVdg0L{@OW@x z+t645O;9!-b zqX*HzzyPLc5{t!fyWPys&l3y=35Ub1t*tRLGef;zXK`_n$;nAl=`>gt3=UQeJ$v>G zfx!2?ehn@cr>CcQy1nw*@RFfua2_4PHaR*T``VS0Q%c>WwN sFMsW5&N|$Hj&`$ytMNbWSqGN(F9BF!LvT{w`2YX_07*qoM6N<$f*CG%3jhEB literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-13.9.png b/web/www/routino/icons/limit-13.9.png new file mode 100644 index 0000000000000000000000000000000000000000..66af065f9aa1d51c587b78aff504232bd45b2e6a GIT binary patch literal 760 zcmVGD!v#O0;5yey?sMOCYj8=2?%w6ye0 z*=$R-+LwuLsnw)xwxy+|XYxEBDwUE-BtFP-{-5X;$B`0=4=RV`F23Mx#MA8pSXSTCEn2Nq-;Rwzx%>@w<6j5{ zf8^y$@cB4BJ*Bg=lRzNA_4PGpXJ_>G_F~&MCnqO_!(r<6I<;Dj@4{hhzaO4FfsY@5 qZEMcj+<>-rvyH3qmG-O+OZzuew_bB^$`cF#0000t{?L5P`+lfcAZAWjz=6Hw66xp}*g*iNxvA${Pk?t7kl7WW*G zMC?S7Lf6f)RN7}IbIx>{oSh}pX)=>JXQ{ML*Ud4Cls{`Fl6WBqrm?&G5y!{xVOc5I zHei~7rU4HRK&u6uoB)kRfI{Irwzj??mHP2g|6=yMEG^4ru_)MVl0xuRgdm$uvRD+f zESH{_9XOsvK`>3rYOqovdp*91UXQF)NXu$42&SK9LdYzacj!2McDrvA4d`}B$LX_N z-k}gO=(>64au@Wxe-jPxJTjNNpzG$9Pft&uF*mn_QVEbG{7$`IhY$ke8;YXf=;#RB+uPXK z*udD>7%nd_I$Qyqev!`xVyW<_4PFj!+@^qze4>!u(=7`-hLbEKO6D{40Se# hxHbOMJ{!WO{RxS~Hv-fMH=zIk002ovPDHLkV1jt8C-?vW literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-130.png b/web/www/routino/icons/limit-130.png new file mode 100644 index 0000000000000000000000000000000000000000..dd4d5c0144db60cc9849f920bea1a261c93beaaf GIT binary patch literal 728 zcmV;}0w?{6P)pX91ZFt-L{Ca=hnDVbCmRs|gn*So@H!X+NoR*bIvAr+F^#>~w?i<{(xHX)fwP_WIS0NU zNaB~{x`rGltwQ0glF84Snv(9_lcuJmWb(5Lg|~8?v|QKtJ?oQ1pJsECSFfJ&@#7QvpFf-KG4R7baA1-;^IQu*pO`dK+Wc4Pv&`kt*^gSK7XV0^S=|_ za(*u5^EX;we<{!NBge@oo&KU~_1{FdRI5@t{Y8$GF`obU@q+vJA5tiQ&j&!IQo-{) zB9RD3M@N*)WxCxi@pzm@qd~b`W_Wm*SS$v2??SzPhf2jjmQ~lzjwI>&`dYiYyPBDq zQK?i?y9m%rle$LJwU|Idj`g}f2 z(?k-%V32%1PcD~ZY;27E{e1#~0Hsoia5&82;bE^OEDO%g9{3p=fj#l*w}p->2JZs6%t`1bAnK>KXK6EM)(9N^aYPy1{DoAxIGA5j~dWZ-iE0000< KMNUMnLSTX}Vq-!8 literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-131.png b/web/www/routino/icons/limit-131.png new file mode 100644 index 0000000000000000000000000000000000000000..d322423522ea9962b1896b966f30483987470adc GIT binary patch literal 678 zcmV;X0$KfuP)#l; zu4OD;3d1a2IJkFaBO?^S7Z4PM(rio%sZStg78+Vm!D^C!7GfO5g#(=r?&{}!T<*E& zfFxcV&y#4Hd6r7s%x2G+N|BS3WGY2wvu7-owrQGqdY<&URv?KG#~H`w<`*0teS)SX zq3eLE03F>kbNqUszlFj%4z8SRa}#D@RQ;SuFZ&G)Uj)oA^H2XpqIC zPfasvS-B_2qsVc_scCgqDrBd_chTvPl?th8bvn-Yqs;e3mdhJ7jDYR-`$SK)+oWLx zESERv`yyzXGtB4DX<7d!dcv~EeEyuKIU{|2c=(2?sgEd?06_o%)M_>OzK=vAfz#7d z92^{=+wCHmOhOQZFdrTs21Z8#+x~!BO#)TbX0=L^yuZKa-rgS5=`?qDcWK);bzSG; z;v#2fXL)>l%>Dg+rqgNe?Cd<>u39Bk)fVBp(@@p0vw|QX9*=`0WLbt`7$_79h{a;K zy}b?d7pN-Wy3-;e5g-}`0QB|sVPRnbnM?+r=V4-E0&{b7Xti2!T^ECcgP5P6M<$a& z5CqS%XcUM2?9ng9t@wq1k+qjf{*y zmSyB}IY^R(<>h6Jjg3Lq^)Me87k&n z!9^lf1V<@!>5x)f+d8>Oz#m{x6hhA8F<=B&5l;OI3I;LcJ$*U^1xtq((hHvLy|3kc z?gtU!*(jxpWyK_!{3_w_vGnwaghC=cJtE=ou_TjU#j;|el>U2G6A_+poFM7+dv;_(d@7eCY0_2Vi3WVY?7n5HFDQ?48yiny-)5!V$t zJQSIla>X<)vF+%iqb72kpqOS}mX}3tZ{@GJy%kwr7BS7dI8Lx8b6t-tEq#(i;-}o) z{GI5Ln;Vft;-@SveG=F8AeQw;VzD!^?SB(JV%s9I*qKL!$3sv`Ftc23F7fMe!rjl`+FuQCs|ut!!Qg^Pfu$r5s5&lG~~hSg~mnz8X6iH z8yjPAa1f;wy}iARjEqn$7P-82;Pv7~DX3KV9bMPi z+1X)zeVvt+6;4i0*x1-0pU*QfF~RuwIL*z?wa1kTD20c%wjzbXGf``6D?Xo(Xf%qh z>&(v1(%IQbC=@~|MK+sdZf=gfy}eqXP=L0!q9!vl-!P1KeE9+%4**wJS2Q&>;rII~ zm&;sVU(?>+j_bOVN+ljE*wN8};rBx-1$TGf>$+!kZa`hTS;wpKpYB;5F6}QbH9y9+ SELs`>0000uO?}3CU)6WNxl1(WuDyxJWcAGB;P1Y<5RXGa;_4z0N8Tp)1D;^Y!Z|PEVIG zj0njj#9|N#z|#}hHk_S7spODK{bY6Z8+Vh@lWEJ^+O~L}4>8SoNhGesvi=S9EXxu}B(B6X=e4a?>oXG*AIN4wDFBMaBA(|l zI5^1p`8mhO$26NwMn*=kZM*9ukq9arhKq~$6pNa+rPLRemWG*`q19?}baceZ$_i6c zQz)g#=kqv@!@8ihs!4iDe?xVf1j7VG}3QVLzy5fK7` z0GUjNR4PR<7(_%~_{Yb`&Pv2$aC0-`!|#XwegOLV`dD0CWO{lU*L4{i8)IQ%fl8%9 zyai=TSvaYO1_%a&G#U-AudfM(Lg>2who{|!d>$Sie)M$DdVB#r jz0DrB#((Ow9yIN5s+v8tnXjJe00000NkvXXu0mjf-5Nf< literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-134.png b/web/www/routino/icons/limit-134.png new file mode 100644 index 0000000000000000000000000000000000000000..b383a4d64046f70e119e66692861b0d2c6195292 GIT binary patch literal 735 zcmV<50wDc~P)DwlgF*G zKk9H*pueWEAx{ebfM*UDyZ zb#?V~qC2jxq-^$9`TT47egL^{Qi;STmCOH6bVs=?B@&bF!4av5vK`t&HW83$W zH4FpGvXDe56e63=;&~ohTU#`nO}4kUX|-C6kB?*g`x0y$E-oGi2nJzr5P*S!0Wz5k zGcz-Ip2y_mBubiw#^ClAUc7+Mpa1UbKkM@b^mRA;*c!j6&-&1`-vBBwUtI;i RNF)FN002ovPDHLkV1nMGTMz&M literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-135.png b/web/www/routino/icons/limit-135.png new file mode 100644 index 0000000000000000000000000000000000000000..b479683db585e3d65e6afaa8ed8901022d95e512 GIT binary patch literal 737 zcmV<70v`Q|P)P#h#ALjt$h%OL&%ZhGKR zbTzc%D3n5%43^He-8v~aItc!P;5ir%nhv@OSHVak$Ci&@z`K1v&+>iW z4&@jO**J0+I&S>o{@GCC^q;)TfQs7O5iLoDmF*mg=hPkowIM1%)icZl`%H|+1v zqwB*=O+g|7ng+jqf#bl@5gZ@8WHR4)`}QNl!#^ML59UH4DY|aU+?+3GXCl5Yf5i7i z&dx;U=6un0TMC8by`w2|-67HSV_97lsZ`{zs8mE&S4DLFSX_6gDf9h+tgPh3H1DKb z{yWh<<+6xr-pR^JPJBOr*mhb{sY@vo{!MgGp&*hXq7Vv&5D_#@!!%7YnGE4@7{_s_)oQ4!N<1E?zrVk^5{U%d+)M^&YlDss0NUHz znVp?wVqyZ%^N7V_7=}TySfo@c(bLnz%*+giVX(2W!QtUyQ`XS|ZEXbbJgC=simIyY z?d`F)w#L%Z65()|^Ye3Ju^7|S)AaTAQLR>+kLz{tJOV_cMI7gmsJFKlP18swlc=i7 z;^HEMgM*l+$-%(^mSr(MK8|4+&4A-TG+I<-Vc`p*&}%+@f+5TR!602-UDRqd zN~IFfXp~SWM5R*U_V$*6fdPWSAl%(SHVf72*Ou;Ci!Y$1x7oth_)mS-f~Nfqs1r6! TufK{+00000NkvXXu0mjf1QBUy literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-136.png b/web/www/routino/icons/limit-136.png new file mode 100644 index 0000000000000000000000000000000000000000..44ab498354b7a10bec5e1c26f4dabd2420dd871f GIT binary patch literal 735 zcmV<50wDc~P)a z%}^zqf)u(qSOiDgt&0eb4uTOOcn-!uB7&=M9gG?@f+2Ul9fE?=p@sB;Z~K1F@O|(5 zLqvFWY+Du6j7ciBDI+6i($gc--!IbBBQi2_CaKh>m}X3DTYZ~VM1&VCE5O>?XR_HT z3?s$@_j)usx0%90PlF5ivD)LuUDk7PTh+!OwWd#~C$8pKx;;JN*PjY+v zccN!*Z$*;HCs|xv6~}QQra3CH*rnw2|0a4SpBIV6F2yuQ)s=ev3j+fmNu@wh0LbNX zIF5s^>ztgNu(!8ItyUu#403*c&hG9m^?IE^Ab`@{4aH(7xtyx5D9Tr+ramw{Oub%b zXJ?1``FREh2T>G-bUKY?S!``>p=lZ$8ygghMUIb;iAJNm_j;jPh3)NkF0QYK35Q>P zR#6l*O+!R*yWJ#{NfLF5+w}GI5s$|yl}g;)+#n+8x=u72C7aDsDwP_twl-*K!G&!@t;SnaRb_vF zpXKFcW@l&V?Cj+3?hako86O|V>-AEpR2r9SHLz`5_KxML!_%qq@zP57%WOEwIr4m5y#P9XB82lify;Cu<)6kol#7) zomdP)A@KX*_7-wE*xQ5seVcgvCsR}3Xm9^j%~#FYY*`>SSC#4Mc}XT8N+ zU7DMlIXF0AYio=9`+II~ZrI%1B%jYyEEZ{OY~+1!FXZ#@$YeBaPEo!xI{JbBekzp; z8yg!;PEOL@-HoCsq|<3^+h%QT4OLaKEDOuBSY2Hu7K;%G1fX1o_4PWH^YeZ}q3X{n zih{1|hzNeapJXygJRZmI_Y)3>Sy@?OczBpZBEjI`;NwVyLU4ZGuj285&j*0l>m`v$ z(9_d{<2ZD7b}}?HM4?dN;^G3Y*Gs8XVt03!v9U3He{%SI;PK$Wap3Nb*Jzr?_VzZ* z%gfBp&QdHEv2B~<<6{iNps%m*`SRTzI1Va?Q6QIlA!=!9sXj~7G$tk{Xl-pJ8jW&v zbVM{7rLL~-IgrbNVH6Y@AODW7e`0YFR26{J(^Kl}>uG9gqFgR>c6Nqg80fmr$;k-~ z4GrkJ{wR8QfOHzJuYc5Z&T6~?HQmh`w#I+zvl=w*Z#AbiN8vQ@6aWAK07*qoM6N<$ Ef|!L+$p8QV literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-138.png b/web/www/routino/icons/limit-138.png new file mode 100644 index 0000000000000000000000000000000000000000..fce9d0c11e93da34825005e0e4ceea81bf99685a GIT binary patch literal 735 zcmV<50wDc~P)#Aa!3CU!(B^EoE;bD={QIX+ckyz|pGMQ~L&4jqF`g>Lp5uUNE2w%Ru=ip!- z!-$ehg06$#4-XGeF2ms=935GtQr}rw*(4hM@tl7)mr8Ll3{&RjJUKlT@jUq>o+om8 zDl#|ciD8&hD#f21Esr>BSe`}@{P=sMW;q>qjc=$kz~=+t=H`Y#AV4S-qF%34 ztyT$#!)Tg@<2X2u!@$4*!C(*`A0eBCySr~~-Lp1dKwEFKjji#Y`m7C2`x9^3Jf*43 RqEY|=002ovPDHLkV1nxkUxWYv literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-139.png b/web/www/routino/icons/limit-139.png new file mode 100644 index 0000000000000000000000000000000000000000..4fa82d9be47a6a7192e1d85e8021ee913f468b01 GIT binary patch literal 735 zcmV<50wDc~P)tw;jO|()7ot*FQ2kT71Yqm@O+QhGELWf+y$aBAzFI#PdYX z&qWp%JTVMYEGv2MXp0QTe$$+8&kjv%p zJdc5a0rL4ghlhtW8Vw?m2)1o=baX^lR~LhWgAj?p#YI24oT|Q6l=mzy4ly}NtJPwE zf1kCrHRAC&ilUIsW^o*cy}dn}%_ciLJ6v5|ad2?J^z<~(LLsQv;rsV4ALa5SiNwRt zDvClN5I{uGG>vpRO)8b5zrUY+K2I|mnHdU&0;N(3UDvt2y=8TE70a?{wOVaiPY?M0_;6ilGve8! zZU~3N1OfqSwHlR5g`uG#G);rMJIH3CR{Pq~J?roVbo4em*c$(-&pObwzX9k|I!x!- Ru=fA}002ovPDHLkV1lpuP&WVo literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-14.0.png b/web/www/routino/icons/limit-14.0.png new file mode 100644 index 0000000000000000000000000000000000000000..7f93fa2f36bb665233218a1df3e6aa4e37bc005b GIT binary patch literal 750 zcmVHR%x9=<*Lp#i(1DOZD z<@@l#Ip;+Z-#o|Zk!`1x&Aw0~@lLUrbnBKBi%E&ZJ7u#kWZNk@PR~EHh9o-FYC}AG z_A@VEKE$$ydGG*|N$~sO(Yi)oMestSv1sOXuhMF3!)TmTwp6PPUCUh8qkO)g zrKK-AJNrJ-4QFT4($W{@^98xC2if+tQmOZKCkWn9-K5{Uq?ySvN&{ywo-48t%0s8*|NZf;_lCO_V}1J&vel*>IRnXIZ<)Rm-C zsid*7F|DqyN|NgJx@Kl(l*we|{ymVx!$Zx@&1rUaR_SzFM;|^&rIM6PRy{PEcbS|- zj)P$sgu`I~uCA^q6bdvN4NTMQ{K~d%d_Es*Yioo;A^v>*8t&bLX7jEGuNQiI>GgWa zWHQ9#aRAoW*V)+EV03hpqoX7C_Vxg1x7+yre){_Q2nK_+S}pMT!0W|}pKA&fPex9kRDOOfi7#|-e9*>jD94FlC>I{ z%}Q#TNy|#Tdi0HKdy<+~WvN8A+x!siHd!i>npUN4Pxj5E3{lt1%;kKxS|2ldrPU&H zIiI>-rj#MjG*_5TKhU!N&FB@&BGc&yn&wLQYp?eWGc#)_6aYm*rBZ>E6653J0D!Bj zE8N`NKvh*JiW2;Fpj%h~&dxreQVD~}afKtDdNyBIom7MIy*#GMJyA2LK!$9pU)+7_+mpxWB*0_4PFXAQp=SI^XyEWa398#D|H~tHbqNvc& zvzJ!A^-w(K(4}~7dyzd9dr_}}N+CE4VGPtL;vv{V1Y=N$NNQ*D_Rv*!mpybL`@na6 zpUd}sFOvA%cwSJBlTj}BS|cMB#pBYwdr~|ujf_;3%e|K4WaN3le`XCy+)%6a^YZ0W zK75$PvIcnc2-0b|bqiW8D3{^mN7&x3F*f!)&z}9pz`*fM{>JROrYy_R%#5#{9m)6g zulT;Svm?#S__8cVu4`U9eu!$dep%MGmX@URbNv+O=hD)WWLev))%t(Pd_SOtg}mnG z+B!S?Ingy|XVTnUTMG+$`F;R7&O>D~U*x*~O>~XxN}0?TInKl2i*MhSNhE$HmjlC~ zSS;fEKCxH~fW5sv4h{~8$Kx1=f$#g2N+k+~0-op5pH5@!?%t+Y45D=Ulit14m87+` zHKkH1t*)+0lIr!kve~R`+m`?5LJEa~Ow*KY+p;W6f4Hvn{=Jk=e+tlOJis)O=V2HI z(P$KatE(&W`8=nmr-Z{{48s7R-EK2IJ3ejkkqiPjQ6B>;N0YV{&M2Lh!*tX5^ z@Gt=D>+5W6Y>-SQsaC7(@9zVU&1M-I8e)Eap25LE#$xXf~U)+id{u-nm2c`EzJC-*)uQI{X10 j{ml-x#{bl39cbF`kj7^k{cn-&00000NkvXXu0mjfUv+m4 literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-14.3.png b/web/www/routino/icons/limit-14.3.png new file mode 100644 index 0000000000000000000000000000000000000000..8c9986f1865ea49282322b8cf1d8079a3714d742 GIT binary patch literal 751 zcmV zp5=YtACmaj*mj>xGp>C8g{G$JibSLb52Q#$nwqLBpMN3KjLWwB{+l%;tCHjB ze{meCT9p+0g->qNI)Tu8ZGSA{}Rj^je6nNU3bPL}m;qFXFWipSr{G!uQle*Cz?!-qeT&x2u5 zE|+l}hhQ)W!0zrY`}_MuA`uM3pxfW(Smr^wP*2US`BVsXR z+ZcvHAP@lH=H`ZCu}G`c!sGE^7zO|X0|Vr8IWn0He!ri;KYV~#49?CTxp2Fozn^}$ zn`}1A+Ae zl2|yd>!)F;%w|s*kKa(1$&C$CmdSYhhS}^14MU~t`v0tzNW#OiLO4GDhRe$XXj&M% zyMUqqtE<4<8*qIMTwMY6x`qAyUpPAYiE!BV>OE)EOi}HhfCF&dG@BB^P0T2^RgCWMcLLW$}0l*8fYjOGl7WI8=% zp-`d_KF~0BsHzV%&3`kRW16I@KF~0B{NE>&AK2Xdifk5;B-Cm(2qCb(z77Dmy}d=N z)q*U`kR-|LwWq5pq-OIAYBfKoC{32jbVybz6-J{`o}HbMB%hz3xwp5+WHL$d;X@X5 zk}Q`=MQQpl7;GVt09_Z7Bw=lB4FKRc4oamGUS3`h3cA{MgTa;$fdH_w zf|Wo3$z&3-SPTGgadClawTeh2f=;J{ySqC8K($(3)bHPrcDs#Er-SKq3Pn*oz1!^qjsv(Zyq?X`b)A(;#k-%MpY!nWkiA}yxm=F@ ze&5sc`TRo9z)a?py6z?Z>FJ5%@z^sPkH>s`e5B(z?Dcw_OePCDg&=jE%w$fNnzJQO lz*1*(iCg18?Xx9p+TR#k!GhT$yA}Wd002ovPDHLkV1hX~EoJ}! literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-14.5.png b/web/www/routino/icons/limit-14.5.png new file mode 100644 index 0000000000000000000000000000000000000000..c605734648113b93adac9aec8d1f529c1a547c20 GIT binary patch literal 756 zcmVBe;dc~%C?irW}nJ5-zgT8u3wj8G08OFDVu#N+fK@Hy#LG^lDMK)8|3lh zUwHofCoF4-J9i+FfIt8~euV99c=ZZ4Hfr3x`;3PVe`9ERi9e9+TF zkIzRYlVN;(9DtRT6;@YQ85tR2e}A9d-CY1ep%By4)1*=(uLY_V)HTI5?o$Y%)AN%+%BreSLkLw%ZqZ$H7By?_0LFuSC&k zlwdGOI-RCaD3HtLn3Pn9y9cjdLvQa}L-+6hPB{EC zPo99sL!;3k7z`2$g#c)`+jKe|B9RD3M@I|{3~+XKMyu7LzrUYgAOH^@!0G89UA?m| me?V7%vx}|qKlND`n)XkE;AFkgMs1RTMWbM<1SuK{2^htcinK59_3y$l%nY;WK<0zH zxo|!%_nh-1iN77!9guCOmCL=+%*=a5qte5NQZy>f%)D1F_eQpzmg^4uv(}JAk7je6 zSFe7iTz-aSP4MIiB$IIO9(?%%2M6%>E$r?#nV z`;zDBU-3L?e_vWz@nl)Hs@2&$$E~Q@9G7M7YJFYmbo4`XI@0>OWLdjvHpg#ep663O zU(o96H(g!*nCOnHD`|E0oAUXBJkN)0`?1pLkE&Mxo9K>eRZ6En%C;X5{L<}i5Q{w} zmjlC~QmNp19^r5pfTN=$>h(I&XcWURdUrcJJ9N8U#*;~mTI~Uq$^c3x4^=G6kyI*` zG&wn`&CN|oQoG&O+}xb9*{r-j8*+Abrp3iYWilBpFE8suqal?_QZjky$cNt#gM$qE{baLQ zrlzI<*xK4+dwZLBJkIIqDaXgh0Cc-u!r?HfREl!BOzrRxOcVTme7G)r{mNgFL?93# zkx0;LwP>|koSdA{Xfzla8e({On1zJ}B9RE4PoKbXz;*F4GE(ERb8~Y;I2`8u{G73|F&xLC-EK2FI(qwOFJ8ds&%gI|&icFoecjDIrpEu2 cXMIT8@07$}`Fp{6ZU6uP07*qoM6N<$g86u8ZvX%Q literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-14.7.png b/web/www/routino/icons/limit-14.7.png new file mode 100644 index 0000000000000000000000000000000000000000..f422d233018300ac8f82224d8ffec35364cab779 GIT binary patch literal 751 zcmVG)@}|CLN*l} z?mtj&gL}E)lA)!zwq2w{u!}ktX)OfL6oYLHB9cI9g2rD^k!sKJbBEN>d+E?Z?gQWP z^9=9zeLqOzU(a!xWZN+%63;a_m{%ks-MlG9BGTYsUWvqW*>+5h)AZl0A&ENWatBYI z{Keb1Q!%$EEZW=Sz&W?lYBnU_3my$pFiIqlW9WH=m(|K zx{$QExTxOVUM(yvNRq16szygg6_3Z|e*ch6CZnmTDUFSdsjI6?Z{EF=UcZ*2(GOk@ z4{tF%j2s8UFbD>N09;&LkV>UEI5?oay&c0a0I)0z%d(iCpQpdSpF5TXr4k$--tyq} zg5Qtd>m?qK)7RGrz|ztZ%gf93^z=|H7AX`8^>r$hO02D|F*7s6A3h)W{owWD!ExaH zoF9>d&*x)kXoyOsLZwn+XJ?1q-CbPQWp#Cxa5&7sz(6fJcSG`3W~)fU<$8;m+pc>aPfqO z8ypMQ;C>w9kfEh}?-scs*hOa{5DdQO@G8_QA{e2Nh%rR)4^5h1zc+Zw-El(?(hof2 z^I4wn^Fm}aw0juTfZ^;TnJ9~2Htj~+?kurxOIL8;VRIZj;7X4}7aElJ!{D)sZ~ z)lcMdKVaJfOin^H3J)K`r%$lE3;8^3ZIyWT>~~(ie9gc>`F{W2?7A`8wxhW@qn#be z82u;4NIN^y+?KHUbULH? z`7f%~zD{&UttQRSe^EM}kue_RI8POif0XO~KhYhoE5+j<U4f zi$bA*F@{hm1i=3OK8J^ggu`Jh%fc8#E|(*p&tr_?yGR6UZ?B6&p$$c&@0HD}A!&7W zRYOBVT3K0ofM6}_fV}qAr?c; zCYEIp3xK4q+Pz+q z$t1(W!vL(Ut+Bqo&fwr6#bS|zg989gPfzLU>SB9)n`*U+-|vS955ViiL$e9>I)6tJ zKA(?BBtp4drd%#_bace=@iCj5n?xcJmX?<2=;)wWEJC9J%_bgtdiL1ey%%+NcjNc_ znVFd(o6V9=rh6jr>8kNIbmU8fsv6BCKCy8UFhlAv-JG=uLOfX^5zYA zJX9(b{C+>3ot*$&U0rc=b3-T;;_U2)Ludk`qYV`K@(h&&2ix+Tt`CChK*5VIn k>2J2MHNH`wwV-ML1Ym$?>V!F>F#rGn07*qoM6N<$f=rQS#sB~S literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-14.9.png b/web/www/routino/icons/limit-14.9.png new file mode 100644 index 0000000000000000000000000000000000000000..d95a0c2b245fc83ba295bf1abd64629aed1e22d3 GIT binary patch literal 748 zcmV!pS7~>V9YRiemr5bz4df}A?dj7L&%t-Yc7ZCC5q0^Sb`oYe=F^tv1Mu z7eDdl%@5f25a~1|642cZpFY9vF1&pU<#LV3kALIYvtJk*I&Sy3&90l2Z97_6@U^oe z`M&-Y-!BN24JvF1D1*<>mVU3VVCsP%3qyL}E{cf^H-g zi$x6&4{L30O_Efv*EKaYrA#Ix|F4B?Z*MD|PHTRCUenXl`lDKvibW}r*b8uWHcB#y zJP*S#2#3P}+}zxd&*wQkJ;kyt48v$o+S}X9>gp<{Y4Ury438ec+1Y3S(}ZAd z4cSY_3>J6WMLL8``U4b%katP~@dGCjAsJ#wKoJx3=I$WT#UgyOEOJM(WtFLL9%WA5!;pu1t}V} zWtx`q`K7MoNmMS6%QSOJCZ$G0Z=%tVl1a%lb1Ij|pJcY}R3ed5EY?xI{x(sUdR>ae zI!Yu`vTY}_tYw8l*UIPrP1GfymqMXyS=O@a^W)guW@k%;Wq zmy=FQhEZ}-tIcB=JsnBH;c(#h`vGXRTBK4bZf54~vV7R4NrJl?qo^ zSG}QT6WVRu1Og`%i}TFR{w5k38NuiC5e|n5hr?tt84eE*Szll84HkyvGEPR z|06p);B@w6s?{n!pO2xTApq{~?zq3dXLNM*CDiG_<|f?Ue(&o$>+=uj>)-6-)c8;H ctPhj+C(!ah98(Gzd;kCd07*qoM6N<$f~Wc`o&W#< literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-140.png b/web/www/routino/icons/limit-140.png new file mode 100644 index 0000000000000000000000000000000000000000..9bce99b1f697255a94c1afdcf0d8cba9bbe3b670 GIT binary patch literal 728 zcmV;}0w?{6P)pCrcHi%Ic!Q3%pvz@fz3A%VNJW>PmnT&!T| zKTw+BE*&$pbZ@&zh2W&CL!l77Sz^Rg5Cnx{91?$klGNK=-wr{cr9%tp17|z$!-4aC zFOvA>G@AiAPF$(fd&Od36^Td>A4-vk6pMXTD)nBD6IZht_&w{B#2uB&5O3bR;`8U{ z*!D0_pMqtypi8snu#2hC!iFz%UF}S62yzLKF&x)<{?uoSr`R)6oHj0YGPGC$qD&jE#)} zu)e;|#>NJtqoW)h9kH{sgK3&{cX!j()kVEtZ^;Y;Iy&&vY{K<5zaxoYFv$4$I4373 z93LO!x-MsDXE=^SqtRewWTbWZ`Wl)|{PgveDV2T^_4f8+nkF+dGvsnPGMNlhQ&TK2 zFB6SMNvG4qVlfhlL@Q7#L0@0lr^Uq&gu^d*_YVAi0IJn0rfCuig#fs^x}sjMGdMU% zqtT#NtI^-zk7=54a|175!sX@1w$53bH=wP%*~ZrRPkq*gru_*Zvsl7JYFL(leQ01uaDcI+!RFq2}ZJhREp0(1G`Y@ATsPTE5@U2T8m* ztyYh0JEVAgTfyL|0s(1aLJ9<=VDMD&__l02q*klv^<6^}9g4*fG;?bGZdsmaS;i(ebh5IC>Yyk&ch0Xw;Eq z*~;Y>o*Iv#VsT8Cb);lcs@3!%YBedDlq~B=#p2jw%yC>wBvOjS+NxGR7J8yum142B z5{Z-?$AxTrQK8Vaa=CvCJ(0^vq0qH#d$H&H!^00ICqEO9gJA%W&1P{Nhrz)?0M5?N zI6prp5C~ux#!Ejs3WdTave_PFngylPlB8y{sZ1uL>FH_h?CeOA%H^__mzNcZL{zWW z-*`zyTR)PptrY| zNF>7S>?{DgySwb|?J+eq#pUHC7Z(>D-)^^`V_q+~-MDDApwZwplJIyu%+JqLDwQae zN?cuC@h*)9v|6|s9zG$TpJQgG literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-142.png b/web/www/routino/icons/limit-142.png new file mode 100644 index 0000000000000000000000000000000000000000..b1b6249341c62d5b9bc5fe0d0687bcb0fcf14cea GIT binary patch literal 728 zcmV;}0w?{6P)pTtBP$t|d0h~m(q5<%k45aBM?%q~I%e=0O| zagjm|?xkafmg3rWP&x!B#YqB1A$SFe0j(goghC3&Kd4|$@9oXM?-FG{7}%eLckoQC_e9!cC%E{A#Z<|Us$ zKgF^-7#af8ga;4c#}6nLVRI92yZZ)4z0n z{&%7q&d;Uk>0e5xGjd%ovh6{|<6l)M{F~^8LP3hhzsj};8(#hV`I^U%pO8v{#{)n< zpT~7wT3T8FI5;@q@bHk%&Q5lAcF5&&IF3U&9L5t0!QS3O^7#g2ntRG-B}vt4Rm;oE z>gnmx($bP7sZyzEcz9UjYOY_9+(ch+11)2?PR+j*gPeW=W^hjEs!X z*Vl*RIOK9UW@cvC*x0BI6pPT-w(rriXCE+(=e&CdUM~QZN`*imKrk2t;PUd4tE(&g zem`etXWT@vy}cbH7=#xu;QRNFb=|W%C!ns`tYd5Zr#`Dg)BXkzI#)@@Tz-uJ0000< KMNUMnLSTZuWLkXy literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-143.png b/web/www/routino/icons/limit-143.png new file mode 100644 index 0000000000000000000000000000000000000000..5b4a58c88f7672adb314ddada2764c378e958179 GIT binary patch literal 725 zcmV;`0xJE9P)!Z;(5}+ zfwZ{j$+jK2?xR~rM^r9HWZQdMUY4$|^jBP6Nz2QUZSSdEj&x+6=Tjz=RVsC(TJ7&d zx72DxD9yyn4!$E4F>7y#t+c|6Z! zU|;}%qoX5^kB=D{8Nqd3c6N4XHk(AFQH)3g4iE2<&-Wn9I#ezvNouuP+T7gK`1rUs zHZ~+ll}bg^)6-g5SkV6dz7mOq=I7@XkH@vUyW4qPE+<*mp^uA;hgjBKWevk16bb>* zYPHB_vs9~9Ow+{g_mfJcNG6j6gF&vZuRASaS#WXj(1&S4e?I_yeSOT$%`q`C0l?PQ z7Tep~jE#+Pa&khW(IA;jQY;p^yu8FT&5o?UA50UIb{m>aen%3$y}e9LO>ug9%K7;@ z>+9>(YBg3?Rw$K9oSmI@E;pOdZsQ{yE>bA`A{rbVBoGKNJ3C7*mm`zOFf%j5>gpBk_|600Ip2Aa z#Eav3LD_av>GZCqr_U9ONn>MDEGA7)pDUf-m2D^GdBNAUh9o+2oME=MK5=~f0m~X; zZVpTndVAsV5l&Cx%cgV$JLY8G~anV<)B>BGnD&Ln%C24Wd zmu1;1787m8Gstm-HT85kG< z;Ns$ftE($wu^5J7{Pv@x7(+vFcJ_{ZK8Q^7Oxdg?so88Qm&<8lVnTa+dy=F|rK0)y zd8JY*wOXwg-fs^no0UxSEI_R`g=uysYZwO6XcT~Evq>hCp<1mH4u`4N>m45og)n}- z1k;3CZ7M)d4@4pWgu`J{sT7lwlK>nX9B_DeNIV{=TrRV-v%}HR5%GAO>+5SSFE5{A zkqGqk5Ww@G(crZt5ekKvnVG?LT^z@uTCI{uBye39*L5kE%g>h^4e&f(^sKGb)m7zk zxsKo8-`C2@iagJI;a~U6(DL#(qS23RZ$lseK&4Wludk2({(b=J^*Rp^4-5_t{^?sS wSX+bp`|n--XIz>% literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-145.png b/web/www/routino/icons/limit-145.png new file mode 100644 index 0000000000000000000000000000000000000000..165c92a57626e5a05cc2d1a1dfa29e797dc5940e GIT binary patch literal 733 zcmV<30wVp1P)nhki}M0Z?XN(&3ON~bdlf-v&DNhOmd<@3KMx+9;LlF5=hZ!+@w>(@6tdh`s}g-{59 zTrNit1axd(cIje4i67ynx;~zlol5kRVtO5ughj7%i0ffaq$?-y8qcw zC`2q41E5~7lgVT_KR+iLjZ!QYsnu#kA`wi}q_3~9ITDry7Z;Diw6#Gr3P49k2eY%Y zjE|24u(r0w`uaK}BO{!goN#)2%JlR!j^nVryv+9Yc2gFOLR%YQ8V$I);YTFV-rml{ z!~|z&XZXI)@$oUk!^2EXP0`!iOSM{UUcR}3MuRZ%_$La5??gR4Js5^TDwQIe&5};1 znVFfvbzQc$ws2jSv9U26$7u!%1&GH#h4kXZzrr)zuY=L;}Mw;Pw_?zJzM^zn0Efi#MR9yV=6l_@DZ$1x@=6lHFPz>|LWa P00000NkvXXu0mjf%C22O literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-146.png b/web/www/routino/icons/limit-146.png new file mode 100644 index 0000000000000000000000000000000000000000..70217c0afa9ed2a683f3db71fb389287af41a220 GIT binary patch literal 731 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 literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-15.1.png b/web/www/routino/icons/limit-15.1.png new file mode 100644 index 0000000000000000000000000000000000000000..f3587578ffc3a03aa192d45d8e59c79748fffbbe GIT binary patch literal 684 zcmV;d0#p5oP)QV4WmGaCH|0A&0{y_+!vPg~%0zBY}vR+`aehkmjgu2Mg&3-sR={et3D` z=XpR9ua@V@G|eOp;~Q1=it#u(HATkbq^e%gFuu_=lk`0K_p>3A2(ax@?CpKR$;n6P z`WTj%0ZjuG1$cP@YBiu#0xA_7tE&ZUZ-2$u*!SRkV79CTb=_ntC0MPJLh!E$K~}3| zDkZ4vCM_$`b96d{lg#HyU9Zr#N4qj1B<6CvOs9P|8gCQzXf()l z+Gj4eOCcoCG?$o6)@fP)ChB2XWHMQ&X)ej1UH1#7r$4|jfKUkKav4GhD2jsP<71qi zo#E!@2IJ%7P!uJ2>t62t{5)`e{to4`3~Jg1i$!)w9vvNVW@d(kLV>rpw^UV?8yg$k z+S+2hUMER*I-MYQczD>oU9m`N+JywiSwJEIdLBZd5Tem2BuT>c^);TJo*>IIR8_^u z$Vl+}yf3;O_1YolXaT&~5{s2MMv*C2F-` ze`sh3gM)*ZoSa0dRDxj`n4O));^HE9c6Q)6&Z``NZfC?|mtZDyz-%_C_{Yabwpy*9 zWz^vP{XL(bpXs_TA08g)y6)?o?~~aqnaLdVwa)t70e$VwK2DAQG|&1lX@39~qJ7Y` SW)(O90000V$ILHM89kR)!Y*9Up_ z>?hv7{Q=h<;=uz*ry&-D&!6G&5XxoP+p9A*waKGLzc4iP`)&T#?E4wHuBZ8VqiR($ zM*oU2Qnf10&l|a}C*RN9IIczY`k-8QPphj^v#D>Q*_2jSCD+|ky*_v?Gse>L@>4A= zv~_XuZK4}4E~JHpww9Nl$`}iI-hE}WHTnL36W!qZQZ`$Y=iLwg{PpV-?%w^LLIFY{ zDwPVx7-F#)+uPgh@9%SRa>DTNFtJz+V+;oe2b4-Bf*@cpohEd2^c|H-7^Tx6^!l~_ zkhHb6rSb7`ZEkMr^z>AY<7jqvR*Q>^s?}g})++R;$J6=qS_E(aZbOrcO9nM{&OrC3{Aqgt&3P%4!u7K`k@dk62|Lto!fNDm*r#I}Fr z`E#%=&d<;3?(U|ir-x><$>rrG0|Nteb#-xec1AQBrPXTDZnps#NF=ZyKZaK8RY(7< n!wu+YH#@i*|I?mzU}^sZbIxGf{<-rG00000NkvXXu0mjf#$;$O literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-15.3.png b/web/www/routino/icons/limit-15.3.png new file mode 100644 index 0000000000000000000000000000000000000000..6844ec9952c2d7d5e923e9e04ccbff93ed9ba2cb GIT binary patch literal 765 zcmVQ9NZ?y?$sn$RxOjnv zLnond6-S}o*r7*fd$&0hI|>dC#X!!FfQis_(7`2`Lo^zRcPQR9AMb&IN|DAP6;*N58fR`_S z;@!I+FwH^YaR`UOavlp8dk$;2(GSJ99oCk!f0*pSM*gNVcv2 z#I~hEL7JbpWtx`q`N*x~i>O>4kZJB|Wksr1^;J}>(#nctntLjj2foN`+tBjz3#C#m zU0!~j=$6Y%DV1tzdHIEG+d!5TQ#4vqKL2f^Tk?4+8ZF7PV$SEEKK;z2N8gi9gTp~K zo5i+mJRT2QTU+FEIgXBw7#bSFNQI2`zVJ`BU)@bHl9>ua1&C!tV?o}L~6+S}VnrBWo5NxWV!e}4Q3kqA^O zj}2TdXltX*}?^D#X=O(KzCeSMwX-CY1iM@LB{5)_L?E-o%`{gub% z!l2oNMuWd2iCV2jFc@TNYKlN0K&4WlR4TE(z0K;J%qC-!f z{EE-_Bd=b8VQ_YKMn^{nUayyGwaV4i6}`Q^xZQ3}PfzLV>!V(;bAEn~KM=t8-~l{+ v3bopA_xfk|+<<%S<~^>)|FmcKu(Y26j!az?Zzi6u00000NkvXXu0mjfEwpX@ literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-15.4.png b/web/www/routino/icons/limit-15.4.png new file mode 100644 index 0000000000000000000000000000000000000000..24b88aa421090d2ad4cd9032e6ec08547ce2f6d4 GIT binary patch literal 735 zcmV<50wDc~P)`g^i7`7#*#5=euUZ@KRNEhQoq|0x1OliV$R>K!(GD zs;bj4yl;+^K4nhbxolflS?cwn7 z5NBs+7#kad)9LK~^~uQz%H=XVJ|AqQ(g);nc2H4DOsDxua(8!^Gcz+xrBXaUKc_6q zTwPt|+S(ec)oOQ{+uK|E{eFf*AqtXAr%6RANoX|Y;q`)+1)I$Vm&*l7l29&}@$m2f zyWI|1mN7g$3;=k1d_*#t#NFK;`uh4{13K|(H0C8Z96*0R`W+60LLtn}%|QqOm&=7< zFbKcjkFBjO92^_~05X{jGMNmfr>Ajoae?orr=1)+9FSmHz{?AMg(OVV#Q69)mY0_? zG&F=pqk&4Lf_A$NMNv?%*HN$6QK?jb*H^%@Ai?cELb2Et4Gs=sU|;|f6BEd0v(PjR zv$L~USXe+Jkw732z|PJNVzC%jR#p%U1_8qW-0mYV8vVxg^={&CZf4d3kxs zr>7^jS}k5(U9r__kt9vi002ovPDHLkV1nJ=P&ohq literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-15.5.png b/web/www/routino/icons/limit-15.5.png new file mode 100644 index 0000000000000000000000000000000000000000..95edb73b1f4d0139b3a80af174e79c21f10ce34a GIT binary patch literal 720 zcmV;>0x$iEP)D6L>Y(ulByb?s3gRE&rh-Q& zK?qcFmr550i?g;{CxtHVYA|>RjSlT#5CnykAV*FTMe)v02hldAgN5{g@AAU?OyAFo zh*0ue*C&=06Vu#~X!N@@H;c5ji8MEhM5EuuG&jVuV&b~KKf8*E@Ju$_!0PH-c6O#P zj7Elsp}!wA4Q_AY_!yE&I5^1i`t@h#=ReTc_~W_%Y)+>K#4s$Gob=@ANW}BxuXvuw z(UHjHq$h@9Njg3737xODW7jiQQk8(6}j)};mg(*i9~{q zjt;7;tC^jhC6~*Q&*zE9e(+RaNM^j^j97U0o50M5w5!AeYNgUtiDl^)-&; z5Q#*n(lkgUpiuZ!)_+#!3n=StmT@)y)1H-KX@3Hb97TS%(pKRB0000E? zr$8&&YoSYrmd>`_I9-eiAKN4$cV`JxX8$eNHqFQ4CAesW=w3`^=DQQ5$-6J26^@B38~aN zx<16ihcG(}UN2l-!RaZa({OZDVsY^UPoKVKXz2T0{?44s&55p?vbN^P$%%;L$X{_B zk&_dVwKYd{-IQEz?$&W5DwPIB*N-Hb6sgzcUexO%$)t#`A4#b+cq4NhRd#k>%ErbI zsnzZ$x}{bV+1U6YJ3BAMaa6=K;}VM%C71g*(Ji^0NGw(q(~P^GS=MtRkw+K?C<>WO z2FG#mdcExL?{j>7%=!5_!^6XPyzz2BOi z=JTgdBoYZUO(Pf#vc0`cxm>1Js}YaK>F)04;^Km2GD$w4C!5VOuj>Ro9{Bjtqf)I- zGdG9WHj1JU2n0}7l|rGw<>e(Vmy2jLN;n(_Ae+rnC=~Gf{b-s-m(K?e9zeA^t>Sh= zM+Y5lH><0wOioVXI1Yh8fR&XM78VxR+uP&t@DKpYvIqu)%+Jr0N~Oqu{R%!GxZSAO zHe6rxD>FMcVX=#a2C`7%{fL04^8cbS@+A`*$PwY7z&X)G)*!j~`5+nZNpbMqa6 zz+>LL0ac|^so?YZ@caGL>vbB92L1j0csw4;=jUBOd00000NkvXXu0mjfBT8b< literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-15.7.png b/web/www/routino/icons/limit-15.7.png new file mode 100644 index 0000000000000000000000000000000000000000..0f374786ff7c2f64c8bd8637171853af98fba597 GIT binary patch literal 760 zcmVpPk*7O=Z|K-Y0l@zWm%5q<~;51OP;5H z#q*^7eQ9palVv%|=g03Hx1wsbTb8w}l@+Pc&==8YNGmInW$mh3?Y@EiEmnQmIIia=D!5=jSy&J*`9{q2J%Wm0rJ= zlF4^L>h=4Kk0aN`=ksA027bSvqoX6PuC53K0+^;rS63GRwrykEHfw8ZjEs!%ooz$8 z4E6ebKfxe`!-RuDW@l#^9v;T?JPgBNYHEs!i3!%%*U4lu%{rCKWipuzi;IhV6$(K( z48b6NToh@#9}H zj30RM0{nhXPfuxUYooorokpX<#l;2jc$`ouM6FgM9*=W&c1AQB#V`!Gxq&B7;PUdf qmiDa0AJEd@Y~gDBPkYvarTq--kX+()-{j-~00008BpL@bdMYX{h>A6Th7{u9rGq#sh>IsQ z+~6Vj9W)TgDe@zc=+W4G``#eR-El(;xd%Su z=b7I3MH2tGdflyDF0NGSt-|4t>g$spK9u_Uq;UA7QmMDf<>IQ>-T(e|N#dSTshiiY zegme zSoorH`Rhb?l*`h>!WS(szmhQ)%H^IY9xuwazfE+9ZAAn$D+Oc zkb{GJQCnLZfk1$PfdMj^45?I#p`jth#>QA%TVr~9nnIz#;^HDBBO^>ClVID>-hSxP zvuD2&4F1TQH(*&@TwLJy`)O%up;D=Ed3i}^XD42-m-F*;y1Kf!zP_ehE)xocXbuG6 v`E#gNe`{#Y8r*<}cC&%2@jvZZ1D5tL@a|&Eh*Wkk00000NkvXXu0mjffvSBx literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-15.9.png b/web/www/routino/icons/limit-15.9.png new file mode 100644 index 0000000000000000000000000000000000000000..a8cbf6fc7881862b7213d96dcd0f2b3d6776ce03 GIT binary patch literal 762 zcmVa25CC+&c8=s=eDBN>*2?214Ewc@=C0g%d&`he$|51l!B|=IJ2H-O-_i+z>#CaVsho`(&Cqt*=X!ioS|UMOt5%Of#oqvF}#qx`x))UTS%{ zp>p}_M0b?S((-acYilp%x(2eWX~p9O+4i@I?yzks9xuqUroBJc>%Z{$@%N-s;PH^n zW^rAYU@*we&JG6$2b`auGcYhfFc_rKXkgnmySuyi{eHS9C*kDeJF;0XibhZL_O1Sw zluoBLIy$QD?QLCLTqqKWXnuZPtE;Oj6bh20!^1-*5(zCVEGU^w>d$;$N~fi0^u(Z4 znj{uOj)TYJAruN>7zSr&XIx)jT(A0KCGYKo1G4FDRA277yZ%+1XapPmNWhR)9S9zA>Z z8==sTynYRa!PV6jEiEmywY5>HRH#;~^z`)L_xriLyd)eBb8~Y;xm>0v93~hD!1L!& stNq^8oHe-tP3>kASL1)$vnDLIk^mm+?S_AFzpo!7SbPl)AzhTzTtZx zkVN44zCz1N(=?B%>o-g!$k|ykksx*bhNgK;%SzMtl|N@ClIY<$6F4~di1YIe7{(-) zmw{9YP*vdh8K~8OQVFP39IUK-$L{VIOiupj&G*ccpvCilSUrls!rMjE#*rlF6Q=-EQOb^c36M+nAr9$MEnlwzjtL z@bG{}qk&8&gJ?9`4NDTxYyu}I?_|{LNu*MN7mZrGR=pDI*jD!r{Q(z`y|3*VnPQxCkKxG)=?W+8VOiERK$jaB*=Fl!e1UCUr`4i5oY4rCr59}$T}Ff=p-*LCsq^n`dkjz}c(E7a)# wJ3D~qee3H#>+=TmbvOIi8vm)!`p~pL0bC^#L{!ejaR2}S07*qoM6N<$f`z*-H~;_u literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-150.png b/web/www/routino/icons/limit-150.png new file mode 100644 index 0000000000000000000000000000000000000000..9cc59421a33b18a4d01044e90eefc2a67118912c GIT binary patch literal 739 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 XY+yM}J}M!O00000NkvXXu0mjf1IJJ6 literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-154.png b/web/www/routino/icons/limit-154.png new file mode 100644 index 0000000000000000000000000000000000000000..1650af36db8fce468a986a724bab440484e2c987 GIT binary patch literal 742 zcmV;hMa*bH?)v@;N9Ne z126yg|3ec0I_H@SeS*Ec3eTUf^XAQ;jE+{j`L4NGd@9>^G&kpJe_wK4 z{U@#~?e9x-bFOULQL*^+!O;;_DkHM(JuNLs^}2qDdRPniQzt`g8Te+@@94Dn@@~eu)pA$V$EK14bS2<3~^ZU1NfAHkVuVk}e z7y#t+d0f}U@AtF4y-lG|;Q08Mv9U4yem{qYhm^}@BGD+uqepOX@C*682U*sEHZ~+l zcXxNn<#L*sn9%zAx=v0`lt?5rJ3Fg|g$13TpQ~1@X?l8EnM_9R!;m&MB+EK5xxAdj zvbvKs41-`Wh-sRX%VnC)CLWK6si`S^J|8P9E7WQ=`uh4X{&@+O1(%nTUc6rL`2gte z?$(JkLDK0o@pzoIwKaBkcPSJK#9}c{Pfs~IIwBsAck*5@Cha!d-ts+? zXfzr`qfusNW(b8sv|24J%c5GXQms}wJ3H%KzP*KZ8!tmcpDC3liN(62P$weshXihE&moAjn+AJy zax0DO-(+k|J|n^>q=$$V<6A+?IKsDr;+-l1|@9 zt#&`rEw!3RI(;K+Yn$SEDq`D7NhH2Yq3~~_TM7k{MB=;Hc2axOY`*2mljm3#C<*|% zTn^9k@caGj@9%Sbe9Xng1))%ggM$N(j*d7#KWB7w6#t_~aC-WXTu#&8D9Sq)7o$v1 z(`+``+uLJhWrc}}2|}R|%gf7LUS8rj4v|QNjg1W|l?t_5jbt*(!+`;)*Wt^T2P)O- zG`imTSw&F@27{=o%Guc&^?Dsm)6jLDYPE{%x@ek4JRTj#l#bTsVDTaoIa9y|E+-N|ng-SU5l~U=BXmD_lzP>(Uu^8EG z7R$1joSbB7X^BiG)2U6AP}IxzaPhOxW2w7 z8jTVN1pcVGxq(-&z;!=$b0lv!;M>06Gko9s z{tyvfoodx3mK7J%+>(KTQ|as!>FyTk>=YRoI2F^}63dE9wd#7CRYZgr3WX5s>t9GF zCozn6hK3*#0k<0-9w3{AR0<9b3JeebVqxJs?d`u`@-OC0CMt$u$;5;s$HyX$BY(wl zM2?R|CMFy)3`;VZ=(D3HDilIu7zeVnBx2k0E^J$5X-UK|4x~^B)ntyN%HrahOiew> z-QD{{&)nUKOiew>;^LY(j*3{;pv2>slF9s==$TALBp$yM%Nlg8R4QNT>G_0df}#MB zPN#7k2e;eJ?(Qx}M@O8UozcWX5qNH7>=b#;|oF2~{FA+cDDmOuc?W!T>SpmKZL zPbBj4vx=hN_xn**m3%%=xm?EOauJP2IX^!qpU>m-`OtM8uh&}}iAV%)Z~Ijm8lb5O zfX2o~#>dC$>+8dD9Q=MiV`F0^5(zdpH`(9cN7FQ7u^7o@l3XrVlQlI#Ljx++Dm*^& z77?LTDiIEc866!((=-%Cp}D!4k&zJsfdIB`*DgOkLbZyDrsc?HUx@;N0A8<`uC6Xp zsT8JZ(%ak1^71m_aG05y8FXDIkx0}6*(_*UPLb*9ANc*B+1LP81>pMn8jr_=&*#Io zZHmPrZEbCMJRa`v@430Tp|!QO_GV8{Fh38a($BiiS)DhauDe;s*7#3-R)?nj1J%_w UhePPr>i_@%07*qoM6N<$f^k(&>i_@% literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-157.png b/web/www/routino/icons/limit-157.png new file mode 100644 index 0000000000000000000000000000000000000000..14168ddea7338a56e80db61e7d83e941ee4ce470 GIT binary patch literal 716 zcmV;-0yF)IP)_x&u-^L!Bz z-n?>I6~hQhIJ_%<|An--i*$8~w6}}+{TC7r?}}jrrCe6uXDuSa3z7=Kphjcm(fU2qt4-eDb-3==%P%Q4L-z=8z%+I^=`FMJI;_&d0 z)zwvcdwXeWYGQeLnQS&op-{l-bP|ijI6FIIe}A7)C{&e|N^o#cXC;^O5eU3Y)?%@s zX&Q>6aCv!2sZ>H$RlHs=)6>(;&d##Ez0JVD0Apig)s+YYAeZwgSgl~Q0Z?CG&&pHN!> literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-158.png b/web/www/routino/icons/limit-158.png new file mode 100644 index 0000000000000000000000000000000000000000..854f737e75f1df04f930d216e855f49e102cf062 GIT binary patch literal 740 zcmV&PZej!zw8r{_h#-o_WQcH=ln&w_;HH9? z4&tO#aTO_cacJo%*n&eBDY`f-27;FiG0-|Ff`gDE61;MQD9PvP5EPUSEu;^e?R%f) zJLg42crulWM{GMOmbEUi*pYN}h(x0z9UUUE*pXP)y4ZG7DizQ3tR^BnQYb|D`0*Wk zdlQ&u8>tk;E`hQu0TX z5;;5+86Q_-nzrO}i3dkjR47EmG!JBPQN(fNuW%fZ#YGX*Jdi>mQk5yC%fiB{Oitd* z)z#mL9=N&^nVh_rg@sj7N=IyaSdz(e$>sh{^gu2rl1!e9Z4Y~v%jNfUcfZE6K+^!o zWHKnF@cDe~?Cg-uW;r=Ip|!OY+qT)?-$yA$Yb1j9@+JKE(MTra@hofF2PP(37#N^j zF0;M8&Ft(fJv}`%H8n9aGsDHj1+MGjx-LsgOE`|h=H@1`!9iZNw?nA}-@m`mDHaEa z#~*)I(=>v?AiAz|e0)r)RKnx&5R1iVY;45q^^!;=@caGT+}u=GA|8igalnh$3-$E? z)Ya87Ha5oA))q=Bg25o8qoefq_p`RPMl>2F6bg~eW+@hn7=}@m)z^d9i%z8icXvET zL~vb~&dyFoMn(vS!|d+vGBh;A%E}6XK!CHev+Co!JE&C935S1@&p#1`LLvNqKV4m2 zq|<3E%c8fpmyL}LPESvno}Q+!ua8tJRSo3x5Dx#;WNPXQ!Qfjye+FF#;PUbk!!T%Q zXuxqCuCK3YX=%YQ3>?S7aU7bPn+XI0aDNYTbKtsPYr1DOzJQwEW(`~8KlND+n)Ww= Whdca%2{h*b0000N&?H~@0*;Mf8 zqLWg^y%xGSSR57Gt()MIQK$rhr^6X&MRXE`6ctHgA{vtO>kt%LI#@^___qIjmjC-+ zM1EB%haOk*#)G0k&v-G383;<_THc`lAK~hGB4dd5Ny;Y;0_xX&U?c`?ZxY47k3It7sZDH387r z*hnIgpufK#&-2iAow2boMn*j%+Euy_@%CUR_6<->uuJtHU3kd)uCyB0SzZS U-7;le&j0`b07*qoM6N<$g1VJZZ2$lO literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-16.0.png b/web/www/routino/icons/limit-16.0.png new file mode 100644 index 0000000000000000000000000000000000000000..9f1244c2828f4a3118afa96c417b1fb02ce96c8c GIT binary patch literal 760 zcmV5+D5QQ4jY3kvL2pRSAxTguYU`IT?+v2RazhVt5B$dW znSSq!B)++3vqP4ZR3`IA@%TrLj!KUnNu#4uJpNIc%o|x&Qq5+^_gR-D9;jBsyn6L3 z`TWnA<_M`2Bog5B!Iv*^cnI&_!QNh#bb5W8?zlr}ab)7(?F8orY`j-mDSms(wI z>EhzYME6`=NUN(Yt*^h7;~2=YW|T};R4o2C(LKeYluTA+Su-7*w*4EAAOA!q11=YZ zLIKBd2nK^39UZZ=vx9Bhgu`K6E*Ai$Qi*VA_l5~1{s`>eO&CSgzolfhoPoJb*PD&(7293rO zrlwG{iOc085C~ux2K)Q_93LO!_xqWcn4r768vx6)@Or&$Z*SxG``Lg09-cmhM&pTr z+YKHM9=DsNr6r=#D6LkD+uK`$!64Jq)8zAc%H=Wuwr%6{`RMQOr>Ccf>+5Uqdcp0+ zpxK14U-_Qi-d_6p`k0-aB@_x#tJSDfDuhBI8jS{)WpQ(J!`Rr^UB21GU|^uk;o*ZM z6bj+>dP${HjE|4Awzftj5+N3gvAMa)!~5g= zeLxcLmgD%SX&DxadrT(J8IP0G(_}nOCX?qZ7Wb%W89I*dW35CI9)=Oa?(R3#>tCU& zlgMTPMFB=efY(=`-3E@2fo9Xd@^TehTR$;5`OB;KoOL}#Rn?fw3AS3K5d14bkgXP( z%L%HgMqN)0Jl>29BSuwivQi;?J${N_kE~QkRc+ERVsBe zIy!=F+la+tkR-`_Yfm>n51gKc&}jHTMLA=wMvLU(;UN==1gq65@9*!qwzkIA)m3h6 zZ1C#piX>@SmZv*7ICwioZ5J151$NKs@=H}+$x-KlsLO2{oI-N$nUPq_X0RRL7fpTC`%HU3kc4IycN166=|6oCzH QeE$jpm;e9( literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-16.2.png b/web/www/routino/icons/limit-16.2.png new file mode 100644 index 0000000000000000000000000000000000000000..b3edeed92c735b4aaf3ce39d1b8a6459d26c73d0 GIT binary patch literal 758 zcmV|Gc(rHK};LaWR_6_#-;Qf2p-mWn@xz5w4f6~|Y^)`QNcATUv%hvq7r*c{H zJpC)4CzZ?6{Jba2vgJ6*8^^V%R*T89wzaY%ouBKcI6s$GRwT>XR;?DhmU*73rKRUu zSnzdr_H&{e&d#KT1z$@`&*gb0vh9aTrK)nA|0cS@aimnLD%*b8_ItDW2lwy)N-hV6 zL8(;2^E@Jv2>bi{Y;JDSY&MC-Vi<;j=Xvbz?oucea9x*JB7sqYUY#p7|UudnOq=twg&Gn$^B*6i%84i66{Nrgf|$z)R5Y*vw8U8Ja zm;?e43=#|k$Y!&QjEvy>K37*)L?RI;CMGBri&QEV05X{j!^6WYE-o@OG{j^k1Azbp z0+_fiTwd}cU0q#tcXu;2HAOTUrCzU7tybCE+G2TmnX$1k^7;Jr#+R4ix|sCzRM^|Q zl|-Xa+S}Vnr_&4!43N*~iO1uNj*harx=OiR2B1(Vkk9A&>%#~5_z`+~Duy0CdQCX| zn3pfXG^y9?baZqO3Wd11xZvdEgx=m>g25oi$HxSNK^lz)zV8F@%iX(#pFM*{<4sHN oti>PD(%)=hYy3}r)`F(}4w~p(6CU0AfdBvi07*qoM6N<$f)2lM3;+NC literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-16.3.png b/web/www/routino/icons/limit-16.3.png new file mode 100644 index 0000000000000000000000000000000000000000..0d80cd4866fcfe4246a71ec3bc74755a9e271532 GIT binary patch literal 761 zcmVz#eiUu1WX`HE&)fuL0!B+ z!=bnpuHq=1H+JaJy?2{Kv3mxGKp|%=msmkiB~OE)+ACT?z|?QJQMI8?0`{35e$Q(Idv zmC5vUef@2sJFc&#Os1!;t(UTG6BUbb#bOneO8-rCN2w&mVigsO@qwRiZ-3^=lkdo8 z!7wNk3fQ)d&*$Ux^pspK$L;Mc!C(->FzEGq93LNZaBx7o-DV;Z!LY0m3WWg_iG0-C zx9UjR-``g_9M<06o~qTVmX?;ZxVWg5l@*UJ><1CPgpX__1z9dUMchTH9CdU~4S;b8z=E*F_hhIBfO+wJDhW)q@O zxV(I7;&6b|iPPa=d3l+cnHhS$9-U4HpU=nq{5<)59?P--2!%qVQYkFU;_B)O$KQDz z4otdTXt(*Ak&zL+UM~v^3;6wh>h(I6N`+i5$Ii|U8yg#VyDVsmqoa5&8D>@2&xyG%_@F*rEL`uaNKnAd-f|H z&kwwQ4W>!GUT0`%2)El!v)QE4XfQfDiqq-j;^Km_u`yb$7L7)Oz@tZa9zKNU&*Agu r-}?GzeQrQsyV=Lp_@DNy4@>(OwPsjMI8q!*00000NkvXXu0mjfydY|Z literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-16.4.png b/web/www/routino/icons/limit-16.4.png new file mode 100644 index 0000000000000000000000000000000000000000..c6c0038a50c865de48320993d94eec3dadb3a6d9 GIT binary patch literal 734 zcmV<40wMj0P)M-5Qw+URnVMVL`kKTiGc~_{W!Cbgo#-+;QZiSK0h8_&T~K# ze>`2cP*DPm#eUM~yP(@m&drf-H|g_TFc$ksMF~*XEgxqEk{D30yRg6i4cY8h$np$= zK|qp#@p0hw6(|;gvoj!{uOk#X#NOTy%*-?f^8>T0`YFo_BN2mz0%;ihD~3T93S=Z= zP?i;{s{h^57uD-7%5t8`B>DWzPx1UrCX=Kr=UK12`ZB{1nMkA=jhbw?KPP&p-6o?^ zlZix{h9QEAvcf>%ma6)1qIXo43f-v~&N~MBowF-A20HIW}A(yj&l2qdHF?%FWPEP3cdU<$w$ouN4nYtwH8llM6mfojj%u}v z$;nB0JRVF;OaK77-7eDUG@hQGFfuX%0qDo4)mjo^wF0A~7`0jv4u`R@umICE(d+eK zx7)G2yo_u%i*mUP0LWxA$Ye6`cs#hizQ(W1%YF{6R*2Ab;Oz|`vDs{xo}R|q+8P`V z2b#?$YPA|n(}W~RXfzsVG#aSYYCx|C=sH9=on;h@1Bt`oz}VOrg25nWXJ@gyy9=+^ zi^au7q*5t_LLnR-9U+lOU}Iwg>+9=)ssc`D8Qj@9U_3rZ{Nv*zJDm3uhH Q)Bpeg07*qoM6N<$g1uu`2LJ#7 literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-16.5.png b/web/www/routino/icons/limit-16.5.png new file mode 100644 index 0000000000000000000000000000000000000000..b384f7713b00288f3e608910b1779e58038af4e3 GIT binary patch literal 767 zcmVwl9@FJKt`Boc7%9(cX*^(!14!29>GyIbVp!+%JnUeMqF*pii%=i)djVp)?CkAIe2?$<eM_-C=KN!OER^C@@k z{E2CTqL9gCa2y9+*Eu{qWP5v?X0u5+97a(T&d<-;+S+1oZ;wKuz^(iD@qYRgAd_*q zo+!#Q78ZJ&}Vli}G=lJ-T<>h5cr4rR@mB|MW z2q+4?dGm)#r83UM1Y+ALih|$oM^#m_*(^s#M`)TxBoe{r^8rvO6sXthxLhux(I`E^ zAl$tRmCCq^+YKHM9=Dshxj9BhM`^WMTwGkB>pHPmjC49pKA#6b(==vhXPKIsVsmqo z|K7a=pAXz_RBRi*ed8v9K!C2UE~cla2?m3d%VkbZPN-BW3=R%5Gc!YXcQ zQ3-|e92{Irg25mi9UUYR35JG-SXx?QczBqxu`$-x*3dMKot+&_(`001gsDf5;KK(9 zh4P9#e*6l*|1UN+KvgN1%XD^jqG=k9MuXGSQ+j%O@OV6wN+tUG`e-y7)M_<)dwc2h xdLfyFv$NN2{j)YVpsn3(<7)h-J!`|#eg})LKo^zTOdbFL002ovPDHLkV1hIAXA=Mb literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-16.6.png b/web/www/routino/icons/limit-16.6.png new file mode 100644 index 0000000000000000000000000000000000000000..47a435f9cb87caa3be6d6d0c6046758676ea0afa GIT binary patch literal 706 zcmV;z0zLhSP) zlC98+d+pf4(!K5GAo&9v148f|jDo4)AXtJaa*8QMXfJnpJBUKs4lSe)e9H^pGyM2{ zFOujQ&+C(I=T$7e*TlppjgCt9?@Obj(!|6k6^rj>+j)6j->iP0F%tEiL)l-s?{cM9XFz8Gbzj3(dw$yYU!_NwWQTm$+C9TY$k7HzHe$}<&Bn?JG#31 zJJBsySJLuwM=L9D>jPA)eJGbZ)aIsKN#$}`nM_6-8yh-1JJbC9 zyyoWSw79salamuk(&_1GcmDaaR4z-o+@Z_C;2GM8~(=^%L-R1cBm`Ef- zI-RD!zaQZDJRF9H58?9ifk_|$!63mvfQ5wx#>dC$bUL`MODq;+YHEr~r9!=42cX?< zcjt$P5Dr5ifQje9k01QXz`y{5gM-Y@&JvHuaU6$6qd_PX>dISRzk=(6=V3B5ROjH} zP7;sD357xw3I)c-#we9aWHK4D*(_^oYh<%oQmGWBQi*gr&EuIF*xQ4lp}L``Pd^Zi zKI7dxFijlCAsh}9iA1=*zUJcMf+uEj^fr6g8vm)!deF2#0Zdy~5(z&dNB{r;07*qoM6N<$g3nS}i2wiq literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-16.7.png b/web/www/routino/icons/limit-16.7.png new file mode 100644 index 0000000000000000000000000000000000000000..f84dc90055cd73577226f2cf739d07cc6c40511b GIT binary patch literal 761 zcmV*yy)EkYB6*M>~J$xt)4oYKV1*KANWZMZf8XezeU6N=~sf2m? z@;B1y1x#~*WD??Wc<=zee1TjJK74?^y$VmBZ1DW~D+UJsY2{nyY<66xX={Go(ZPY_ zIQmZ;r==I0%mrmbvt{N8aVs#L-<%{{HIN>^9Ccqq)aBGg@px8O-*TNXh?s*e=ogz zC&l9*-JG94VtgDm8n|38{C+=%VX(iy&&kON@pzm_B!bW91HiH@EX!hjeVx(KQGT{8 zD3##+{E>m%4PGx^x0~77Sw==iXf~VN-rf=j1elnZAe~N=&*xj`R4SF&-Q8trX^Bpc z2fSWzyD?}q;O2&J>FMdAx3`zc$w`92AhlYJVzG$hIPC1~5DJBe#bS5MH#g8|U=RxB z$>my-U@%B$XD7*Il1L=N^71m#Xp~qi#@gB%)oPXF<6|t#!sGGWg>pFvh4L;veflSU z|F67$4TeFjR->z{i|+1juCK2-J3FJVuMe-+OSM|1zrUZ0iwk@{AAY|dnoW523_gGU rtF3p|<_~D=Z?>^Deo&vap=tjGMA%q**={p800000NkvXXu0mjf=C5k> literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-16.8.png b/web/www/routino/icons/limit-16.8.png new file mode 100644 index 0000000000000000000000000000000000000000..93b0e11542544f43d2548f2b2e04e72b936f97a7 GIT binary patch literal 762 zcmVtC^L>EJ+GSg@5!6=hk8JIAf4QVA=S+STf+baka4;_6CTU6oR)T~#XKTbXT}T3LCm z#l;)d>pv#Cqh6O57jLw(@>;fSqGIuhlF2W!tp6su!?L7g@{5YaCq2Kn+pl=^=x4H7 zFboQX0=8`v2n0AfI$~>Ui*~zBI2^_>3~bvbm&=jQ=do>@pJFkLQpr!D(1YUfPx|md zElHc3n~Fpt+Su68+1Z&gnT%#;W;8c9r<0QtNz(TAwxZFf=I7@%H8rI@%aT5Rl;ZJE zCbimQ5((7lU>F8IpAXYC+1uOW`1lyF*URYWC?1aofM&BvZ*MP&L;{b;!{67}xACdf z9-BBE;B?}2IGCNCWo&GWo0}V2trmemfXT^8a=9F(QVD?b^K<-uKl}Up)M_={Za27G z;Ba8l=|Ho|_xSyO`uqEto}MNc3{tIDDVNLa?CcPW#aLTgqpz=z)6-LEwV>0%WMH7g z;o-d`7!2Zaxk#tejEs!1yu3^#5+NFmvcA4fCX=CDF0-_>#Q69)>8DS@vS47KWa#Ez<#g2BN-8jS|^dYw=xM4!h4FJ8d) s^`BkMS(h8o)oyliHU6hP>%!9h0~i-x0|Ixik^lez07*qoM6N<$f+?AMx&QzG literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-16.9.png b/web/www/routino/icons/limit-16.9.png new file mode 100644 index 0000000000000000000000000000000000000000..ab44581dbdde5bf62e87b6f27d9f3fe8070202cd GIT binary patch literal 758 zcmVo24v7+_h^_g)@4Z13u5{=@?t$O< zd#3k&k;K2ww!2iRq?OCP)#T(yjf_Z-9!Vo3(&Xev<#KOTsibAwUEls1l6auei1O;y z?-Yx_lF1B{%|bE>Jw5Q{3mhK8hYwILH+cGVix)3mGd%qF!~cWXvQo-qDq2}_bZ{U! zj=qcINCyYf%8H{*rXtHq-8=3?jYd?NOj+ye(&eRoh|5c9eO=0A%4#&CcQVH@wYK(B ztE;YBtsfKJ(`reptFG48UdnMyRH;lWo&F@t`fs9pEK5qKKdDlg?)szM{*%X#e<7Cx z!=O|u;W!SVP>5Qs#`g9$?RJ}JG>TyuxUP$3S?uiW(B0k5;KT$R9raTxb)jVPNble4 zR#Kr*P$H4g*4CCzPfxY5u%P+*c`Yw5>*VA_lC;0SuWUA}#l=O<&d%zjUY805DVaPn zxwx1hl|r_SVHgAg0Zh|mZ*Py|<751OKk;~+zP>&HYPA}{V35ttO*|eCd*w1bc>)&~ z6DA%Hc)fT%9+sAt7#kbIbzN?6ZwZA$OifKuEEcI&s{o`@DXy-r$mjD|mPNbW2A>Z+ z9!zW-zJBFf`uqC{27}DZ%n%NTX*QeG>vbH*As&x2Iy%bG&=5bzVt0YIZA=CRsvI6Z zNW$STKA(?lHcKoPBcIQcNF*2^A7^7@0|3``+1=e`Zf=hB^fXu&3=C8aJ$v?!K;Spt zyaCgs*=*9=+l$}t=lc4Z^Ye2C2M6(by_}t$5s5^&xw)a$YVlJfLXX!A&!5BR&wq6^ oXB}=pN4wd<)%c(GtOHB?4{DEGGbB{*Q~&?~07*qoM6N<$g1qWvw*UYD literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-16.png b/web/www/routino/icons/limit-16.png new file mode 100644 index 0000000000000000000000000000000000000000..e0b8fe45e211f3b1816e834d5aebed6448302471 GIT binary patch literal 648 zcmV;30(bq1P)PKkW z64GfvRe{-A;Q1M7wScQDpk6nzvs1?L@i#0j{d}!|H5av^q^Q`XUoTX0dp}eBR+;@IKLm!GO%? z9Ttlx6ha1deT%8oJq_dEL=y~yOr`Fr>sy{L9^_@-m2Tb*E iPH}4dr+GGoN&5@IJv7DMpT?^I0000ow6bnm^Jg7^nG1S}MSC(AKlDmXb5)4>FVqNzzv-VQ;br9%t34}9DA zK78=~elL>v?zCDxa-5{H*>_4LK5Beidh$dXAD0q|kIH7>$#IfuwR(Qc`Xuo{r4r-K zn^(Ml{|B}`Mmi0a1&pqihJ6c=wbbKs% zo_>nwNyo?1+L|ZZcI3Lx?j0Rbr4o~EA8Bh#y1v#gaeXaqZArF$q)H{$k$Ik>&CSeoc~TwO`)>uqgrzLw`1$Z=9iCO^q_e@}Fe>q^PwCpk{4=dWh-Z{qRiWV7J& z0Z=Fu@H`LGG&wyz<>26eX0u5w7NcA)lh5br>+2&Ljl$!{aB?z0q0oaY>qL8dlB7nX zq5b`RO;1m2XJcV4%*Ct22sL9O-_ z%X;`(pU+1m62UMGip3&lXJ>@NVP@4ken?|F7X`0N<&2e~mi0it9LLr8Rh6sg1+}_@HWPt$m_F~X#!QCA{ zV_;x_!NEc1=jSm^lVC7NtyaTv9P0Ht6B84ii+6X>YGE)kQl?b;PBJ_^Odt>-olY}3 zImyPx2Jv{D?d@%*rl!c{awHN7QmIrYP%6R5NZF?sFa9MG`IEPA!7u>0yu2hB3=$59 zxw*NaTCEa|M(OYGr(Um9tyUQw9VHkHLc0wwU&5C!|8;fFy1W5h-OVnx#{bl3U1-{G Xej7{SLIHno00000NkvXXu0mjf2@Y8H literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-17.0.png b/web/www/routino/icons/limit-17.0.png new file mode 100644 index 0000000000000000000000000000000000000000..40f9c66788a9d0251b99480745133ccdf7d25c4c GIT binary patch literal 741 zcmV9F!hDlm-W-WU{7Q?u|?{EywBjch)C~7L7(fuV4Sp`}gx0 z#sJTrK`I5|Fns$4dwcNV18i?M$YlN`pZ|k_fxlb%mbp?HlVO;en{&0hE4i+Iit9?d zyVBg8E5k5Vsf^t_?nI47zYJqrtEP3u$##GK_6C8vS=N*Y#^<<&_o|Je{Ba zn&_VMb7^70)5^*#xvn3X=D5=7uc}o3o9LcOMM|f?$~4D2UfA|a9zA+aE(bmz<#HL% z^SHdcWMgB4{r!DvwHlE~gucE$0IJn0rBaDtFvxF-1XQaJD3?1>Dpl3Hce<5SEEY8~ zGNMAEpq-r^&CkzkdU{$zLqjT+N|L0*!$Zx?%xG$AN|{VXU+Q(KSd>z!svpaG%-9%m z9DF_>Jv}|Ru1h+dW_^8~+1XjL*({TjlK_~eNhlPeP${mBEjwLEhi@@M59qG%fd8GZfxK~y-)wUjYSBT*EEPm(ba#f3PPLIefDpoB>=!U($9+1n+-}(Z8VaQ-H3DIC-BPg>X42jy9nR#=zkPNt5xRCweHIH)-kNfUD zAc;4}^CC11m4(6%Q>hjc3376hOeDxus>MQKhlZij^CExOhDaj7vc|Br^$jN{S!mig zmX-iT0R{(wmlvSX0M5>UTFt`p@-M8f|HSzC@1Q<#HqA6O&0r=YSg(^p@I!vb}d z5!5t;rkU<}yc${77&WcNa+&OO_$fLavRo!Ltwzfldo>dx%u;ESxtz~-`*TJ;+HEqI z^I0lwQiw2U81q!sTbky-8TBwtQdMth81s?uuKNR1Q(sXi0HF|cU5D@cczk@s;o%{g z%_dr{7KVq15s$}%UhBFJAq3*dB+zVrfv!hDMY&+LN{8gZ!2zeIr&*~~cz%A)Y&Of4 zl@(4*Oz`OFh$QJaPN3V{+k1UowMr_=g#_E4Lplw59zvlIMn*;;gn+85*x%pB_VzZC z$s`sR7lYsL=>il5u zuFJc-JG!p>R_FUJ!xP-nwY34mwPGCvlIkv|IWrF(WF-E zyp5e^UG?OWK}t1&(O2Tz{-N`L>~&3x1B`=)H$)BJp> zQb`I!eU&hjN+oH2K9p^H@_qBxaU-hL`efUCT3eGYFZE4aUP^0gl5Ov)R_nWwg<)JP zE6=pJ80q5T+eEiqTu6(HkycioDGcMt^B&5wKFIg~o9Gtbmn`dpJnv!K&!0d4!u|U{ zkjp_VMzL5ViXyJAuGrbxpY}HohcFB|I5@y{U4kH>FOwm5bo3p?VjIe2 z-s{yXT}#^D-qz62khZqAw7L_bv%bDgsZ?q{l+WkcefJK^Wk{!wVtV}eHw@!PUc7*Kob&T@ zIy*b*?(XLF^psR8g<%*3LBQGB8OdalMx)XEitq2<#d!J@8jasuI%h53fR^rN3tQuV d>a!L!?LR2MVU-7bt;qlY002ovPDHLkV1l^JZ0-O6 literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-17.3.png b/web/www/routino/icons/limit-17.3.png new file mode 100644 index 0000000000000000000000000000000000000000..9d3e0d5e44270500c86c4903732980e2c5595eba GIT binary patch literal 744 zcmVP)$?BybwU@{OcU2OQxAnKL1LSlNF7ONKc+fBO}t}WJUS>E170Oj??wutWOecYPDfrzWkN< z?-wwP2&ojrV({n@eE9-LNATeT930fhWZv-n`5#0gf3@>%vt`9)7^dduUD>wey814z zE7`U*Kkv#gOj%a^!SPL0s}0LA4z#u=-Q4JhxVe$m)+EC?P^~unP3F3Ot**XMF6Zg` z`o}~MTwhDMoTt^*7jj)cGR>qCiBGbu|0jCDvZO@flT0(&^;@g;J5QhfLOu^ZAH`x3 z&-1vwy=8ZIm*eAODwPVoy}b+!4B&Ylhlht03I&?YCXrYSU%C94VzCRwVjuPPt?ni5 z?CfZ4Y)o5QTPl@GT3A@n%*>3U(WnZAf+WedZKYBv&CSgznM~@>Qb~IEPKw1o`l;8S z5sxFs!RPbQ-`|hxx+D?_Ha9n!ot-6{%`!bb4L~RqBA3gN$z*MdwpCKNHdi|N7 zKmfYC=?(DO}Sj=>gtNXKY0QH0yqvd zn|zPGy*FMbK z;N;|lsi`S~!65Z|osEqRqR}Y9V36hIWrl`^NTt(YSr87F(bCdut*o>ge|dSSMx!A~ zy12N|-QAresnu%f{QO+4R!faWL#L;wx@$Bf&y!YGq@|_T9qn0%KcJ((*}>KLNqg3T arTq$zh>BkHc6Ckw0000~H4Qqv?xqigi}3iNu(;bGG2C4IgEqtP{Lnncrd{yi&@M2k{s0IRFtu(ub2EDvIC z4iH74s|$F21#&sy-~h;EO7Q!CVR`u{1_#Tn`IcGNrzp!BgF%bgENNN%5X&O7Suz;3 zD9ajked^uO6qQN?l;sQ)39?q>r>ND)M1qv%3`?bfrp&S&jK`A zLR?&2QR$|R2KEmyGLjaobsZ=H%aJhhv4s^I&P!t8@Ju@ZRXJBf&-=ryuINgc6WE-@pv#XF@Z*- zf%Efo*tQK(6j3ghQ7)Hpb#(q>L8H=?Ne}8|^dc97PyuH2U^Yb%F(l89( z+}zMG43cEMUgzE2U2`rLBO{UZw&twO7tq$*Y-4Nur#@>#)BXlY(vzE{9Z%-~0000< KMNUMnLSTZL8bN6Q literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-17.5.png b/web/www/routino/icons/limit-17.5.png new file mode 100644 index 0000000000000000000000000000000000000000..d832cc9d4b98b4f8d54ac1c8d2744170426f93e5 GIT binary patch literal 750 zcmV6vc9kL)sj{VoSX`3T`Tz zt%EMD_#1={y@P{;?dBCqet{^oDT)W7QD}=O2mvKG2nRz9p*82^?H~qv>Cg+g4}9bM zOz-z1iJ#7O{j%-2lF2tR&9Cb0llS*xSqTeEApc?f=yBHFG92D9f@nHRb8xK=M5O z63>$k4y36mPnKmXlNr2q+=z0yHd)r5mX@SKLBB|2pBgNxqvh9ZfR#%_%=+Qj}1_0RI z-KC?W1H&*#r_&gQf#2^Zkx0F;vTE3&rvM)S?}KI zT2d;NQg?T^Ha0f2zrU~P>1mCRk1HCDYHMptl5~80EYmbKF)^X}`FWk?^HM4$#bTcW zIL-qG2a)Sy7zQmZEqI>>(I}&%qqMfRa#bqbDrK0omb5$x86$%AqvssnPWvN<~78azrxwm!gS)D(iuD@Bw g)%c(GtPV^20~RlRoJ#FXApigX07*qoM6N<$f-9a}#sB~S literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-17.6.png b/web/www/routino/icons/limit-17.6.png new file mode 100644 index 0000000000000000000000000000000000000000..44aa02c96bb2ff707570cb524a2a71e837fd2943 GIT binary patch literal 748 zcmVgF{P4<5KNRnoA1ZzPz?0ap%-!=c*pmd z&N)cpr?c%QnPyzcsL5Bf)5{HZ?DMM*gIan{F|PhFZKVrIh!4lVVIhob#!FE-8OEMgSEX`UzeTw$t*%Ojv8Q6O`-jYNyjod#rTKYR zH#fg0y65IbnxA*Ivhqrf<3*V^Q5C{NpdV0#%))tm!(H)E7$>koB$uyx@>_qS1t0pOx zN~yoUUmF`6IygAc+}xZdCMML^*Qf36ZAnt0P|)=Bw5Fz}G&3`!e?EPZQYk4GJMmH~ zJ!WVK*)|@JhtAGU9LFIZkF&nM&gA4Ii9~|Y(NO^Q_xCwJKc}OkgGeMoTWc#kc><-< zV=q1*w6xIT^N~m-7#JA9bzJ}og#x>~yDTg$;P?9huq=x}Ai(hOFzIxf+{p>Fwt~-x z7u$w!-}n_fJ3E9zA)?VJwOWmfiwpdIKV4m2jE|2K3Tvu9NmKGP^HZ*4qZa_o3*}&EK epZ2T)OZy!FhK7Us9&;=J0000V literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-17.7.png b/web/www/routino/icons/limit-17.7.png new file mode 100644 index 0000000000000000000000000000000000000000..59add225a8282d232f93729f24defb5062fb3206 GIT binary patch literal 668 zcmV;N0%QG&P)IQj?NQewv~Ably%I?{F-<>qb}~3Si$l|uaKqv|3~$(PuWh zO(A4Z*Q1O?o@f~VCi;kBkdep}bv-IEncT<3#5e5j0X|ZEcM+ zGc!CsJ|;<)%jIEyeohvPWH@{csw!Ep^S#$&u~_8d;vx%$f-^-vpJyhMVYk~In$F>t zMb_)2s#1pA4S2oqy4_e`U&q|s9Qyq}0O0ZQ5v5WI8yg!K8yj=hX^49~fY%GS-H>71 z!23Hs%gM;U;Pv$tTU%RretsU}00x(#C^uj-xzBXk+4z^27rwo{ktCbVCOe%DNwU}LvC(MI zvaBIaA;@%^OeXh7x@RN4fRWzj2*1XE`e!5fw7&s4*rQNMF#m}F0000|v)6~n_nI{zKBss<^`3L_E0e4Z47?;-@5&y$ge zpe!p?)!>`sRaC7GQI@kzB*=Q5e?`4cCK9A9XIZTdy~>0zGaldJ%8J2-$ZYyDk({~RFrAU=XU!G#>PG(76VKsWHK2ThJnY&N1UFX z;_~tmrBVq_rxODM0}w(Wl}aIJ3mh34;n~?4NivyC((Cndd3l+kP>A1El{`5i1A!|uG;IpOAgJpwnM~;K z?}rcqk|g2a-~fw@iwK9qn3 zVN6U+z%UE|K&4W_`T03kS65-T+W`QzS`99j3%Ohlnx>(zuMe=>0jm{e=sM7B;ysed zB;0N{yk0L_trl)>Zg6pN0l(jmt*tFMolca?WuVmpbRA~6-9;1%9l_w>ARG<{0O0!i z8lg}KHk%EareS+~8%s+|s8lLgUth=M?P)h~z{FnmC;G`}5u)2JYz4gWLnZ@%K#c z`yz>doaZ&jwoRqe?=&=&S9iDc{JGTKEe#Fjl}^8tZJYAEhJXKtBvGSO>g4U)pZWA@ z2FvOqnS^*8o;-oOJ2*Uq&z~WeEAi^p2VTGajjpagYyUN~<0NERwx*|j9UMr$uW#b} z(!qf=J?+b~Y&lNi(eWTEl{#fvIV~?sH#hn&Zf>OIWy!K~DwR4PWWFEP($X8v&IY=^ z{yxzo*VoeQY@ns3H}d^3vh87+W>Jpw-$ai%j%1oe+4gY5FRuG5FJAmaIt`%^*=&{| z2&hylY;0_BbaX^MpQpLGnYOkzf*`+9><+uPI3%#6mz$JNu*qs`4tNz(rQzLLqLrlzJeHa4a|3I!>Xk>c^=Fy-3 zuY8NGtu11)7y|5Q}|5^YibuxL8a4)zy`5Z*L_@ zmzS5SR;!XE*L8JqaUs`rRjpQ4C=~S9-JKK!(&C~tKmWe2IjeI6>e|gZuEzhgXLVTG ZzX7C!j^cU_ZtVa7002ovPDHLkV1ha_YexV8 literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-17.png b/web/www/routino/icons/limit-17.png new file mode 100644 index 0000000000000000000000000000000000000000..7c46a9fc91f395900f926666bf26d8a6ec077749 GIT binary patch literal 636 zcmV-?0)zdDP)l=f(dHr*1elb$^HYe ziT7oX9a^GmyC*jgZ(bFmKpZ-Zl1bMfm_osgtR)(T$#`6_S|x?xn+QQxt7JSb zXc#6fYw5}HD6;J-8b*bg4B6@MU35BRCPNxVg|VC9Zi_kPrMjFDFz}?-Cf&Q}rPryKDbAVgpKkc&tY}(&| Wj4`Rtf}1x00000Hmg zT(~Su#Qj)YbGVg9j!O6LNu#4uI{i_F!fRPpPOjVW&#WPdJDSa5UcC5) zw{L&OG)H*+7%~|Mh2YBcE8FqJ@a3 zR4RC$M^8@=+uPfe%Vj*zV`yjy!!Q7-*XwL;Z4n3r7)Yg{Uhk$-=|GuGU2op#T2iT0 z()jqeHa9o5v$LaADy8}Pc}-7GYj1B)l5})*q}kb7&CJXwpU>;~(KsKADtE-F4%S!+(%OV&Ivazv2G#X{^-8*>r5N!K_55FHe zJL&ZMnVXvz#9?ma5{k9}0000`3Mmu>jS_c@2y5ZtXAqlQAi=^a z%^A`kM=A^N3uq-G!SA3F2w78Xf`1CDEtEYGmIM?ryF2?_k;`)4UEx9Q2h$AC`(v1S z=LJc;S*{zTrlpw6?K7FYVLVRG%#iUonM~d=m)obNrRciB-)kk3@G;FP?CpKV+1V$k z>NM8Y09gixhJe>spxFc}6`)o#vA%wUot1jfh5ae8`+ zN~HoJ1STgZAxV;d*S*|_g$3a9GKy+72+Hym%VpXmPfkuaH#f)Q<71wmpL20>k(-;F zTwPt|#l;0l(zb0scX)W%Jzcp>%JNkJmbC;$0bLi8Bw=i93?yOOHUNKb=jY+NE*>5p5RFDruh(H&7KVq10RZ7}*w1;M z*Od(p0-+EBa9yC&!LN`+y_T!!TgmHvXW~0bCaW#A4TIHvRpH zi3yC3j$&(T3x;7}dwUy+L;{IK0;N(3mSy?5NF>rNYBqsb>>A8w517yU9sl_F$fu{L zpKa8lVHkXVex~C%yuZJv<2Y|~o=4{MWHx)y*FNj>59sUP?BmqZu}d9Ag<8h z!Cbs-)msnC>Zym8;Yi)oKH>tbMJmNuH-~!tgdfkR;i*Ez>lW&1Pj;mJVE3diz#Nr9OtJ z*B@Y-$oDY}gG3^MB(AQmXm4-FG)-c$7?+oq05qFT=H}+u+}tFQNbq~50;UP|`hyVR zFhn9m!eO%6EMsG1_`c8S=_y@ZU6e{C>h(I&XcT}# zd>_7i;jc)dR4OqwHO0op2A!RqoSd9ctybCD* zR@BqeLp&a5adDAKrNYwE63JweWHQP6`a0!u835b1$>;O@_WnJ5_yE1V$A%t1eoZ2= z$jg@y3UPjZPBa=N9*^UB9-imX*Vji!M+awTXG9_q8jS|cW)pz#?%pNw^eHqNzqItv nTHJt^cC&@6@jvZZ3zqgzK1^o?o}cHD00000NkvXXu0mjfokelc literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-18.3.png b/web/www/routino/icons/limit-18.3.png new file mode 100644 index 0000000000000000000000000000000000000000..0cfd86607d6ea26c08607c50a00472b109cd80a9 GIT binary patch literal 759 zcmV?SYp8lXh;kC-;glxO#pIJi^_cR*AynOis z@7{e+Dm6kn4Y3$JcmS;y934Ti2>bgDve{pG{`@CKMt;A~-T7p z+S=OE#KeR)H#fDnx2LJ8DdloG&Cbs1;NU=#RIOH(PN$X2<&;b&^;@MPy?raiV((ov zn@@8`#YG~K2TiG&d|^htyYW6%S*x!9}@KY;n_3z`0B>0_Y+P|K0;9@ zv9bclGSJ@-yuJeYJdjEOnM@g8?^kSZf5GHrr90m>Ynq#iqSEgd%;iWS_(y~wb2-xQ z7gQ9Ln&y6YbVTLy1QjL2Xq2qi`B&8IWHd@DN`~d~L`NosiIGU0fk2zJ+TV%Zsny6p zpv_1mP9aR7sxHyvxu&N5o9G=)BR!sLs_K&YbE_4@^z;XWLVzS8olZjtfzi=X93LMe zl}bSff${NiNRrh3>&wea6pKYTJRV4ef(_}k8I~rT zJU2ErxV*f~v$M1AG7k?ATwPtI&*!5c$wY#b<$?*->H^$u&@doL5=KTwKoZSn6IQDg zZnqnQgM)Z^c>w@CKR+WLkK^(25mu`e63~fHwYp$}#RBy8q0eH0&*#J3+#C$U!0qiV zY&ILR*(|EnDh37y007Bk63Ju|Gcz;LbsgWXt~xoiSYU!-0B>*j8Is6mvsheQ#LmtR zhK7bvEEZ8JmC$atAP$ z>FJ5BR*N?`H*B?9B*{jj!MnS=&RjT527~)O%~_8xpr^Ol!`Apueb$4f{SLt%n8qM` R^WFde002ovPDHLkV1nJ+QFs6V literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-18.5.png b/web/www/routino/icons/limit-18.5.png new file mode 100644 index 0000000000000000000000000000000000000000..fad46e3dea17a958e7961e37b078f8a66f0afa47 GIT binary patch literal 760 zcmVad1@RAXQ=x~0 zix_Cdy;Qq&XmQqd>tYLCoNXHn9tVvM?O-SbETjfGb6^au-u3GsN?SU#kUsE@?=yYx zi-_>cxvpPKGcAU(A)5A4`uap}+z{#O6VbGfVi+4@nrU%e|L<8vM7X9@>gLIld%Srw zgRb{5HU>jO5DdcCudu%l*(~htmbiWU1^4eirKji1b^h9%&!-)x^7B7pSp5fib|z!(e+(fUKVj2`70bpWO-Rc*LS5<>b{hDo==vR9?R_P zcd1nVPIN`3A~HMsU6z&}i|6?e(;Sg>`m^No|0cR3pBG7|KZ|LO_#a$cJmluhTNnl? z3b|Yk&+}+$X<>VNn`}0V=XrE>b)hHtt~9eqEIN%J3LJA)2Ar8oZtUIQ64cf z({V94$m>_H$Ye6a<8e+-PFP%A#Bm%pH#gBVjo#i~ip3%e3k#IXWh#{lBcr246$M_t zZ17>*gQQZ3>!K(Mkw^p);r#rZhK2@GsTARGm|Cp{K%r2eTCL*u`_VLw_E-#(NwDof zAAtZgHqsafFf}#Bz`y{m>vD8-L^K-3vMg-dCKL(*psFg9laq{(kF&nM&bzm7AsmK4 z03WUk-@frPB7$XEB$G*2S669nZsy?NfWyN>Y}=;4zn_VT3EJA)IIUJM^RA1J*47W~ z?_Y~zu^6hVGB-Czu~=k&ex5`kK_Zc0Wn~3bRoU6u!7vQs@i^mm?!f!^(AxSzk-K-F z5sA#Pwgx^Q$H&KnLLpRD#c>=Q$DzHwou;NH%H=Yhot>PWo#8kR9UUEnf$n>KY0v7gv_AlZb57Wm-ZN1E0000y6?Swj-{G#X>P zeEB2!{12GsI8UBHDg_T7z^6}easq_{933^7oBN&T&wpWj{LlORy}43J%QP*muDUut zmRwi=it9?p$I|MmE7P=8sif~5x1vU4Os09H?QO}n^-b8ew7o5v=8+nWv0ItzdbG9m zLThVZbbbA8qC2jyrM0y$+S+;{*YzOFdaO*Qu1e*XJY(dc(% zvtSq$i$z@5B@_yAaBx7OP{4IvMn^|638CPW9%E z+LCg)oF*qHwYRsY!^1-*5(%xWtY~(2R;5x&l5}x#p~b~TEi5c(d3jlXynip{a#AXF z>Y>$oL^_Qe2g5K327^eV-EPy{+e!Z)>Woc=Nsi`R($KmqwlEJ}2%H=YxRtvx14?w5WVQ6TGnVA{#`8?IrQ}Fx2 z>&1iPz~|3=jU>wDGRb6;-Q8UV1_r3rYSim>d_EtcP>A{YdBWi^_J8}KX>%4je9uLiC6Tjb2AP~T|ZEV{n5{cmR`MA2eVq|25cDqfp*(4H)-0tkzGx+%N rx31nec#uRoXX2as()R3`I5uk9`!2T_}}$r`NA_ zEopmuTa%NM+S=MuKA%@Inbg9F(h$)eN8A7BArgtYPGn$yad3qEG)}nb90mF>1n>VEU49>*?bTn z7=&<`a4^XH{5(@rQ+S@o+1VL=eSMTlC7R79kw~Onr&_JX{{B8ID=Tz|LJ$r^Fh~H; zgR3jPMiQk`iP_m%a=9G6y}g{Cp5izTzVEZUyGuMCCz(v%2ClBa^9T@+zo$}Ziv|V; zh(@EVuCC%Z4r^;`BoYY{i3GV^jz*)w$;kFG#U+thK9JfxS*$}hgd8IH#hL~DSZ0$TSt4=;ScEO jZ+37s{--_bz|#H&GMr_*--rfA00000NkvXXu0mjfP#|r% literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-18.8.png b/web/www/routino/icons/limit-18.8.png new file mode 100644 index 0000000000000000000000000000000000000000..f1bbcdd2a9e88097fca35d46e343eb6aabcc93c4 GIT binary patch literal 709 zcmV;$0y_PPP)WpeG$h@=}Mvh8gxElHlIzrypRr6tL>w^gkUT*-_HXmRnC=H|YuUjIAM zHTAkQH}_qOi?3u%06ESBWirQd-G38ZfT3=tkox85|`LmQt?FXpU?vqX<-^a2nVzC&KXf~U)x3`l{ zr-?)&TwY!R&}cMn=jZ2F=TocQ4-gDOM+Y6jAk)*+jEs!n`#vWpCvZ|?zn zdpDw_;8qobotK6(VM3%$JumgeW* z6N@eI_ALYgoSmH!4u^?Gqj;W&=Xu2AaYCUGr>Cd%_4RRaaY4OaCmxT}8HvEtr*MA$ rp`~}$;ty!)Z?>>C{!^c|plN>qyKHFnZYWKf00000NkvXXu0mjfN;^|N literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-18.9.png b/web/www/routino/icons/limit-18.9.png new file mode 100644 index 0000000000000000000000000000000000000000..18daefced962268a98ab85605eefc2bea66282da GIT binary patch literal 759 zcmVRhFkU8)z zzq7jcB8k79>$b_Z)5>OFYkd5@1_q>u52b+tX?*;>vf03LIFyp3QwN=&hzKL(BJ>({r|n$aZFj3t%U_o`}>mT z>0j|YX@6f@Sny<7wj9U2bKHt5l|ETkNo#9Ty{>PfUYFL^B+DwPQt7*ud7e+Jt1q;; z*i^0dZK6ABHED6NsnyjN@;o21?MbE6ALTgzO>~FjNa^%P+4f}H&yB`9j~@L%HVcM9 zu~@|OJYumJJ3BiR3I#mRBauj87zWK|6UTAb-rgn<2+%t=28V}T6pL*rl{(a$H@cCO z%jGmQG^DMqE#>ohjgF3Letuq4Q&ZaA-IXNm?d>U($!KKLYpTo=PIh(@DG;^yXtKp=o=nsjt@aCLPBz|qkWu~>}F%}xA%Kf9$8Jbnz->X;9| zAA&)Gem`?_bBv6P;JPknXJ>SEbx|&tsaC6m!(jkS)8z8P$J;n1p1_uWj9v&uE^`Jamms zyf2HV4lUhlZ;MF(Lxmtt9!9}BdkAKO7#s*9ZC<~72-}^dg%;coyr=j3eBS5qJ`YI3 zactW~P0O-aJYgzz!+4xrUMAylGL^bvv3NpF%hI-8f7VJQ@rq$A;ppfSE-pSoRb$BI z07U_2XMv|DpxXr+4WQLBu(kCa2M1pfi~V@5e>LlRhN`MDpBLNV zszzPUOdKyF!&suKwpgiWHOoL&dv@uH#d28 zb>-}?UMCf$C&M(?p(u`yNJ5e%ghC;Z#Ar0a%*+fjnGF1XKOP?+9eF4UV4CYP+-|_@ zb=-M89&B%KV{L5>wr%6??hfH_80~f&rfI_G^EqW+FW`1VhHV4GA^rwQwA*c@(`i(z zRRjV7^!t5hXgCCH8#1ENYjnGBist6#5DW%UDwWXh_p!IPheRTQTrTGfcDq0{dM$Bx z_Y|Q}3CG8PEITsy_xJGmdDKEeJzynYQkJ1t73Kd4rJC6)TKm+zTvJ1^6;w6x@DZ%^_( z{VSd)?d?fROP);Al5OYj9JiuYYh0$eqqQ~Z@>1W#<)yT?CYk1rTCMS0ndkYny82Yh z%U{&#e4FTwPDff^{-V{@r}8{MvaDGZ3Lj+K|4npKprYio;vfdNKGM|*p2Hk)j2Zr;A%zYopkcQhISl*={s@}>TgRIk@H zJw2^jt)_#60~L!!Ei5dkTrTVA=tz=ue0;3AxjB`~WtB=LoqYT#)$39&*YwkFKOmn+ zu8Ys-BN~n3^Z7VDJY;xyn2n7M48ve|cNYN5vIqu))M_;%kqEnQ-oV3$&~88QV;C?v z$e>}cxVT6rlfiXe^7%YhS68g8tYF(Vj^hB}I1Zsuh*&Jf(9jUq*VhmXf?;6by72ik zU*q@tnVOnnY;25^lM_;@6sM=B#N%<=?KYNWadUISu$1JQ&HVg4>2#X)^>t=uX2@hRRI638*(|e<9>LqUkVw4q>G9*=h(>?p`E&65 zIX^!q91atSMCf+AbUGc9$s~b5fU~nRlF1~F<8X0t!N|x6;ZO*kJb`Za_rCtKK3_mz jZ?liB@jvxhADZ@Wk)c?}3y5fQ00000NkvXXu0mjf_%3n| literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-19.1.png b/web/www/routino/icons/limit-19.1.png new file mode 100644 index 0000000000000000000000000000000000000000..5b1f015b6d466f1a34cf5e02dc93fe644050e3aa GIT binary patch literal 681 zcmV;a0#^NrP)lfG8!f0@hfJtyEM%Ng$TW^l}IAMwkNT@{RKxypJ5mg zB$I%q0Rsa-vkBB{z{v?vsn}Rr`hks&?}$Wx2K7O*W$82wlc|))YL)an{uR$7t5q_U z@@N<)Elclabb@Sql7>-Xp+Ghod>4%dStyW(QK4;5cEUVQWbl=BHT4PEEFeiJm&@=x52K@_uq+FQ zhll9v>%;i?IDV(=eVm^M&d)!fTn>SncEM7KZITBE2b`Ii;r{+U&(6-6OeVRyy2|C{ zW!~J}kR;pfcHk9@#m?(WB~sHaWH`o-;rjX-BO@c&+1Y`jC^$Vm z4Sv7t1!x-JI14fq1qg=`RurtQtzm9%4nhd%x{jx(C#q<1-A*X>KQL0SCHM;RMqBG9M$90aaB&)3{ssA^RT zL;WjZC{?S{+*~Nn^A!Z{wc|=us}0KYcC@@KU0mp!xVVs(mnF~JQLQ$3B@07ai;Isn zKi^Wl{%xXb>UC*;zNN**#|lFm`Tjj+vmX@%|4npF5J=hVNBRD}$fIWS7w+8ofnpIX zi%O+J7>4xp^biCATU%Rnbac?y*9SlthV1R_Q7)G;#xR)4U>zQQN2L-$nan%Ac%i=~ zZES34WMo9^>+3o=I8ZK^)6C3_rlzKJe0(fPDwoT0T~~!dL7wMnHwdJcFQrW8oz40A zU0fF#gJoI7<8ds@;^^pz?(S~Z*4A(whuz&>09vgUv$M0TuC5Y~$NBTa2XI|DKfi0^ zI1r5zbsP$X0%K!i7-MiYRN{{}Z{Y1)NF)v|-M{}E@%T?XeG0bC z+1VMfSd6Z&E*gyn^?IHD{(d5n2&bo~M59p}jRvh&3xMx$-zNU>Av7A#+xlm1Za`bR i*~ZoQpZ2T`OZyKMiCi}r^!SMY0000(0JV@eS=Qv&!3JK+MFEu`1*5IHNjY@-q()f5;x!g+?3JE!m_sgsyi90Hl0iHkq ziMMZmz_KEwQV@&5y?aov!~Q44gB9TAt@^@z2j?1zNnw@o3EK07c z|HO5rVo{o%b!Axv*>?Qa@mW-<49K#!wYVrmEF5TVT*tX5)<|aO$kKW#109vgUdwY9qY;4eIG>F7v7^Tv8i+#}R*ZND+ z`ue&?M@O}~x~hYN1Eo?aO-)T{Vq!u^M@N#RVzH=HDy5m386}fR{r>*F^yZBei+%8L zdHINV961h#VGs-kF${yl!$Z2dx>#9R!8A>Fc6I>h?Cd0)&63Gv2m}KB`RNnH<8XQT z$b)G@M+Y6I$@KIzV`F1Djzc^i=jP^y`T2Ql+osuU0x�Ogf#WR4Q?Gb%ps)9@E6c zaiG!QOFSMABO@d9_4RRld`u(~;pF6m-Q8W**49{BTB2Mob9#CTkq9^r9zvlK`}=p2 zo}M23em|j52*+{Q+S+1ra*}8?%JT9uLqkLOd_Lyp=IHP5CzVcvZ9^zjGW7WID}uov zdGP`~9?s9t@%#M*0s(5Z8r5o*a5#+D>*eh1jBq$iybj4y*e2)aPw8@zZa3+2k@NGXjKwyo>pq&M?R{1vi5}JJB-YozVt@Y&G|dUW zA5c|ba1dy>fnpIjJOuLjDguEYSX=uJr?b|}_sl|gscAaHVT*+VX<7U$mPHl{WH@Y5 z({u{qeRFh0)#@ZQEze|`5sLwmgj_BM%d#*!Itn2K_V)H* zx7#s3K91ihm&-UmKZncbgH$SgLM~?mRkg%ShOZ=dcXv5GJNH| zMZwY05dh%%`5EbS8V?T-u-omBfNp*2^*I@e0t^gbKv571g)loi3)3{=^?LF6_=wfj zRR|$q7zO|!o6RDd&BEjH;PUbkKhMs(JyaCPFiqg)1@Dn%86J-ZGcz-on3#am>BQC5 z6*`>`R8>W-Rzs~;L#0vyUS9#zgbat{1jS-cGB!4bp`jr-91fVKiGza!EH5v^?RF!T zN+A#kU}t9si9`ZROG^j_gMbi#!*K#eqg#x}dyT)lyJM@>B1t|yJ+V@$u-$IcFbv+@ z+|V!#l4PsZ;_dBiHy4kS(dbrRd)DU*=<99vaW($ap7mjAe*w)VjuWJOX(j*w002ov JPDHLkV1mB$Nwxq0 literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-19.5.png b/web/www/routino/icons/limit-19.5.png new file mode 100644 index 0000000000000000000000000000000000000000..f661c9465d051a1c0f03220b8fbe119226b99e64 GIT binary patch literal 756 zcmVuKktvS#ars8P8SLb9Uf%1V8W9q3&fKrZz}ZY z;z1auc#ot@2cxT2w@w9Jc9m8*d2o^)en`}z4_xg1Zg`z{j`oQn_{hWT^ zFCxM(=Xsjgc3Ld!nIw~+q_0mT9vA8B6Gf-@w@ci<{gS1nC&Xf3FY*^=$4QB4+A=fa%if-d@5>+Y zeUZIAk(n7^Ow$&}Nu4{+M5R)%nC7;utcX;r@>f)=A}cE*rnxPpQtz3}_f=V5ek^ly zKg4zaPIQjzipEfpWQw>$;5IxWQ#bfmg3u zR4SEWQYpmqP!xqwD1@RY92^|b*4D=Q`Z~I!^4heE-hxsH)1qzyOIvf}WlpVzC&-Vv$OvLVtfh6B843c6L&$*U$2vhe{;!k^TJ( zNjMxP7z`4LMDRS1TrS7d)D-b}oYmD;48ve+YYWS=NF)-B-?;-HK0qY$QIWfMpA!n* zMx&gZoKUS+>F(|(7zjWn1GU}ej=NVk;_3U1rHv;$B%G&3UA)P@o|HNg}-?A>~~_Zzx(@rvt^}~%~rIz>FVS} za$S8D*Og9Aq|HrN*=$9YmA-e}i5iUwWwXcH*^%09eG~1rw6h~+v&U*QChlaeYifJ@ zxwf`GtJV58(LJq}w6*nF+uP6Ox+bbr9xIdiAj|r1qI)b$%49yMQhDt8)wX{lk@$gp z9t?wWxs2<&jE;_CSr&(fhYSr35s5_lIoIoT4h{~mZJUWy3Zqv0o^siPQmJ#jeyyIQ zVzH>HsVVL6@9XUBOu1Z6Yiny-T3XWO<)tL)>gr0XtE*aGUe?COhA!W~mx@Ixl{z=M zy?sbJjT{HVFbD(!7=}T;UMCm~vbVQ~*XyNPtpZT3R=K#i!0-2yOeXox=YvO&;P&>R ziPsCm!wh@9tgo*#Gc$wZIHc2QI-L%MLIKONux%Rv+qM}S8)JTco>Hkq?fe{kKJa=m zaUA&ci7zotlVmc<^z=02=yv}c=$#Gt m0|xq=18j}|sm}(`wEqCc?N&Aj`a+5T0000x|PnX9HH2q%ONxtf^l<=w$YUjD9sQ=qhwPj_xjI53{06t2htDD z?)=~CoF7U2Yh2f-Vlk;~_N8L6vIYjEXjB>)kYcg2ve}m^7L#&a-{)CF5=|$ZeGA*$6&^kMgQrh_B@+3onQxjMCn4J|YIfGs?ylr{ z`cFJh+TE3AXFb_=QI3AjA(s*U3+_bN~hDBnVHeV#DtEHjwDHiLP2wLbDEr-)ZpNt{>Q}6mqa4L)zuY?i;FmpL!;4Xp3~{+DO+1xEG#T=FA#uW z5d3~jTodBRm#?qEbuqE5_moOa zNmo}Fp->3RvT$9Od_K?g^fb|El$Dhgs?{oohliw6DFT7OZKzZN%X)9<@#EhJhkxY7 z3ouR2&dvyhLbSKHQ?J*l)oS$i_TuyTs8*}=^z?9XaY0*K8{u#mZf@Yo6R6jJZ|R-2 n_ybz{n=NdOuheHPXxe`O2til5&m_7A00000NkvXXu0mjf^^S8B literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-19.8.png b/web/www/routino/icons/limit-19.8.png new file mode 100644 index 0000000000000000000000000000000000000000..720213e9d693a672177239f6e1e00ed3870b03d6 GIT binary patch literal 758 zcmVB_!a_!_KBI4o+ z4L9gkxQhF6=+dD_SLxk$La~2>5(x&M4o{`lO(GO>ii9Y^qcQLGdxI#n+|UE>1HbY2 znZDmIlKAR8FCfRsDxZI=k&%z;?Uhm~skc`e8TqJu{;eD*E6)pj`)f$zj!LD6*ROx3 zQ1}tsPLj((CIb&2!spMhzYoPCY;RY1_UunyzWj}3@~^x9JG1LrvTa8*GrsorB;VJ+ z;``Fxo-{M#%eEc4u666U5mhQZvh8gxE=m^{`YtXmq{T(awzpNO^xVjN-_*jwE6vV+ zQLXlUqFZV;X?FID78YK~_f6zDPn6AmlI#9A(JihkWwW2;I8OqXr?ayY*LB(0*dP=N5s$|K@O_^`p+K=%#P@x=(`k&u!*)ug0Lo-O=-oSAOIll7 zQ-6QIR##VbaB!epE~m-KNsWz->G=3qlC-|QuHoTfO;1m2baYfZt}DHNFJ&?xOsdt# zSQhd;48tHAjba!EM@L6=bab$?vO+KzWM^jwfJUQ1C=|l7EFzHzf7k0b@u^lHn*@W< z(n3ox$kfyngM)*3o`+>wTwY!>KR=J_x-^?j08UR&X>V_5cXyX+wMsY~h6fKI7{tW$ zpwZx4Ow**VuaAL&0lK@pNhXt&%VoB7s4NnaS)j5;3TV z$s`bs0z*T-(6UO*X31ugZ=%^Gvsu!zO03r>o@7et+}zw^I^Ch;yiN3sJ2Sop60=w{w`9`hZLZ&@>c_MJS~(Iywp=1P%@k zFfcHH@$qp0K&R8`&BI}!T78FN(FI1MRqpTeH_3cH&zYGS?(OaI^z@X;WRlCv%UoDk z;N|5dN%G|6q&KhC$b6oRMyom+jWA*{P)cZ;2Cvr(P1A63aejz%UFPA0Gq2 z-+8G7%*_FfMp%bo0B$$jhJlrp70k}gLQ08PEQY(gJFKm(K?s3%yA1%Z=YxZQVZeZt zz~ducqU$=Qr>Eif`*C%3g+L&HTCE19R8RhXa`I`RrGyTj?+lenS28v>29L)BpU(#= zB?^TCmX?+f3Wbo%<+`6xC={@`xQKWn0SE#3d}kV0SHHvS{ez?)b0{VKJeQb^Y)MtHY Z+CQ*$V52P6jGF)e002ovPDHLkV1gxvM*9E& literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-19.png b/web/www/routino/icons/limit-19.png new file mode 100644 index 0000000000000000000000000000000000000000..01aa7181c1d94a5f3d9af1a4a950779404b4d3ae GIT binary patch literal 646 zcmV;10(t$3P)|EmAYUuNzTlW$t0OdT`-?NpzGR{GW2JyMiS3>-XwN+KjHZJ0~}`x znG9fAKqLa(-vf;XaC!<9O|;u&u}C^jm7X{GC{s#jp|HbTu20{8o9GGOCv&+z z3xypjrGu`!MBBch5dS86LI~2fZ|J&9q0hbE7tGGSM?Mc|8h)ozsX!@(v9U1-A#ikb zgrT7!#N+Xo(8L7LY`#OK5&|u&$#OaH*u%pE4-XGHKR?e>sl>ChGiEXwuC1+cWo3m| zS69LA%4O2BnmSsoMOap#Ba+ZG4Y61Znx^6M@)Dz?quAfyhhZ3~)oOt}EDLD07Ihc~ z5Do|KhKGl-zP^ryg#}0{kxr*^cXx-)%}od)(ChVrvTztM3>c6S=yvfpbX~{X+#C{# z1g@{IF+DvU40OAIl+a4`gm{ZxM|~F)}iOPNxIk_mM~>5Q#)yLj69lwFPuK-v|2720Q@+oy`Gm gjsLXI2C!*=0h~WK4Q~WihX4Qo07*qoM6N<$f^v);WB>pF literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-2.0.png b/web/www/routino/icons/limit-2.0.png new file mode 100644 index 0000000000000000000000000000000000000000..475e604d13cff1062de2aaa578b06c778b8f8fb4 GIT binary patch literal 713 zcmV;)0yh1LP)Nh59D%RF+Kg$?RUw#o}i)#rqebn71Fl(SJ^gM zsgUWkO+^vZ^+b>H3TieZRFo161@iHcZ_?u*u3-hW-ONXl}}3&U7|EW5w!I1bj=*Ku@ogkUg;`}=zU zfDi(HzaJ+jCm0((zWF0i}1i^au7Y;JBsk|fmYb%a787={5M1Uj7# zW@cvkux=NyEO-$P-=JD`>y1VOj^jYnG;D2c;q>$rdwY9`#bTJBpGPj2Lp&ZwDwXQP zs#PEyzVUEl;~Rp(k2pL8yk0y#J;5*x^lTOih0y7AFgZC1(=_q$@PP60aSRU+_l6AY v>;TWt-v_#919reby*a?t_)mE@fTaBeSqWPWq@GId00000NkvXXu0mjfCy+x8 literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-2.1.png b/web/www/routino/icons/limit-2.1.png new file mode 100644 index 0000000000000000000000000000000000000000..0a544a13a7a57433fe5e862618358e0bd75b366f GIT binary patch literal 673 zcmV;S0$%-zP)xK~y-)wUoU}BT*E^&rAlR;w+ee5+Q|xABBcV5#cqi`5V$mHelFB z3qjVHe%Ve77qQiCWQ!pF2LqxIGJ;G3(auH!vxN+aSZLzBIa^2uvLE0=_P}cn+~0lN z_s#`L^c>F%(Xvv^=MR}k+%O&|Cnw2xoJ=Hcn9m>5vQqTC(66-$Nd$77820u);q2@K zgqXn65?~m>;2`k)4Ag4C#RX8QI7p|zV{7XxCMJFa^#R$olN7>YHY-`Jl2Y=Il9H@e z$!u0q2#dCz>@r?KjuWF06&8zRtHn2|)gp^UQiuv2C-w@HQe~mA%UsT9v-vhqSIs7w z%lRx6b}6L_TGk>{sRnKP-#}g2HknE_XjzM)&o3`uFg5ia`8=Q~kW%96>I$Z5!t*@D zVlgO+63n<o4GEI}oWRjUohC&ElUS5(UJDmJfDPBlhD=i{~?00000NkvXX Hu0mjf6Q(hC literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-2.2.png b/web/www/routino/icons/limit-2.2.png new file mode 100644 index 0000000000000000000000000000000000000000..deed784ff3699491081bbc96e5e91932485d34b6 GIT binary patch literal 644 zcmV-~0(%mKCHB73T9~r^7d;(;@SDQiuvID>#rTX|b#;|O2v)1rp`0XHD3FHH@S@e4hhezC>o^Wl zsT2+l4xsBgZf|dgasY$zX|?7xXd2-6yL@G31-)Jm8yg##ot;H8nH70#t14g==BfJ{_ e>7I??(*6SHr)4{D_N!_D0000e literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-2.3.png b/web/www/routino/icons/limit-2.3.png new file mode 100644 index 0000000000000000000000000000000000000000..6087a9b770027cb3fd84fd4106502380fc4293ba GIT binary patch literal 717 zcmV;;0y6!HP)4ZH;5oM&`=Z| zgwm>`P`U^mT8nGjt%HNJg9l=vdV(ea+gT)-F2)}eOhWJO+d-1h(!oOdg?D-2`MfXB z`+UDh;d>)8~dhQ?!8<$DbMTpv)7PBQ{{4uH*cP^ zv-23+j+0JuC8<^U0q4b%aU#Ht6YxV!hAoZ#l_dkW&>Sb z-VM~y<)xI(23lNvE#D6z*G(yzEGeJ=H&8?Qyp&9qJ7 zO-xM4wrw3897vK1g@V%Qv?eDfl}e?wx3|}N-NuGwS;rwNm4{eX^LK+FU}|cL_4Rck zkqD=!rvQY*VY1mQnM{W6?ry5pYHOt|3o4a|CZ-ADaI-!>KF-a}4GRkkjEszs$z%W+ z9v;RtP1e@dxVX5$G|d()90t=Qgy%uM-mLHL?y|JB#OUZKb8~Z;rpd|430qrRtgNgs zJ3C8HPY-8jXDwL04xUGdX!I+^VzXWdiK$#(&DQHYDvYNWxoNvUp|Q00000NkvXXu0mjf|94uy literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-2.4.png b/web/www/routino/icons/limit-2.4.png new file mode 100644 index 0000000000000000000000000000000000000000..94ff741cd04ceffd2190ce3d37f9c3c064468a05 GIT binary patch literal 705 zcmV;y0zUnTP)<8qw9pT0gxcq)}5%*?!HdHEHV z)yBX8m?k`V0zZDh&JGj`u)ba*nf%PVcOPhLD|`MPxmb+JvTO|vx!T^ATvvaU>q^_( z($J7A%d%B0#_EhaP^lD=Wvy#!N~%`%C{?S{)Rbgd>nfEZcQDuWYhog+;o+JtE*=M} z>*7Kh9^Y|CMXuFq3=R%5H#bKp6yoIM1c0lnE3(-v zr>CbhH8nlR7Sn{Yv*&&c1A;-%o=7C<>FHr?Y>e*iZj#9)0J&U_TrNjvXD7$U#~d6S z+{1!FFbw=S4*dM-*$agNlarJ5_VzM5I*MT!9334|tJN?~lXAICxm@-#2JZYFhro14 z$4k1p!aREh!0PHMD=RBhDixNNmIwxeEG#UL$z;f6GBh_gV;BbG2>+5P)r)HKO)!7%C_IhFitfvAU%F84Gc(zaVpz>C)dr&_ap!Ih9uEarIKQ8 z?G+zC{()un@%%ZMCd6X!?Hd$}@aYrm?^np@|K|1UxAgUWZTVZ|LLno|ay302==fL) z0{y3gKsr8_rl$j0ma9S`(`4L(DwUKhYhNoXQnjj|Qnf0rtVou%uSz9#4-0~@mX_XV zW~QOb%bx=^b$Kbx%rvyL^hQAtMy~rz*=$*b!v6y`RVYZ=Y+0`REb`~~?|<>+$?w=U zghB*Cz|qkWj^p6_KB-iSP$)#bUT0@#hr`1|N~IG0{r$vZF?jR{PELN~^XCXM%@b{H zwJvfTN104UlarIOEK3Im2a=?-vojfnp^1qJEi5dkTrNL!x3wji=1CaOdx~kcez(zR zFf}#B#>NJTM1qTp3jj)`64%$)L?RIk!=Sgf_hF<=6Fl!}lxP$>I$HHyE=RpyXK`_n zbUIBwp9dfwk25|#&hYRso12>q4Gl3iHg<37=zwUHFuo6WcP-fN?k>yA%Z!YSFgG_x zG#cgn{2b5o7#tjAbaa%So*rto+Cyh|ci{Vk>F)kQvDm7Y%Vipk29Dz}KR?g*_BN}l ztHk4R_V)I$ZJTsDO)i&vfEA0--Tft`+1d9b5-(U^hj5syt1CRuqZzZVt}bqGZ%HPT z+}zwytyW1UlXP}=HXj*YzJyxsLtE#p%?W60Hru#0e$hT_!>0WKU^-q9KMe&400000 LNkvXXu0mjfFMwF{ literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-2.6.png b/web/www/routino/icons/limit-2.6.png new file mode 100644 index 0000000000000000000000000000000000000000..ca434efa622748e6f97ae2bda14d6c00ae0acf6a GIT binary patch literal 713 zcmV;)0yh1LP)3kK%(FvL;}}Ajzkc4@yQdKe@Z&Fbf+@B5O3&P>|$YcPQ3zlW!^70b0 zEW_4PGQPfuYOM$aD~2WquciZubcJ_}KFe%H2btgo-*=;#POpAUC;cL0EL zxs02e8w?E%ArgsTaB#3UQlbdx`m6wt2k?3wKaogadU_gLTU(f#nnE&}1OOO@fsv6B z%+JrGSS+GmulHbHFW~V&fN27qj^m%5ond!(7jtuS$Y!(fcsyt{8t{6(7#<$R($W(A zem~mnb`RF+0Hz56fj}MAs^d4CP1v>#S(dT6xrvjL6YTBnVP<9q;cyt)Y!;D71c^kV z2dh?rK%nm8#>O}Jd>?Um2nYfm9v-0UI=V6Q`~7%&dcxS)7+zjpaDRV~(b3Uf&ukmm v-Uc2YzxQ>{`ka8iX0wl5<3H`QK5W`w>l9cr$|al(00000NkvXXu0mjfyh}AS literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-2.7.png b/web/www/routino/icons/limit-2.7.png new file mode 100644 index 0000000000000000000000000000000000000000..17ae517148d0503298bd038dda0ac3d4ef4bca12 GIT binary patch literal 701 zcmV;u0z&*L z_|FfLXgj9qqNarykMGj&FH@Gukr7gsNx#3$czl*9Qy^0;SRi=B5GXN|u^&=ZmBC<;3kwTW z6oqGJXC%p7F2`sz%K7WHJ4_@JY&05gW~D;PGM$hl!0on2Wp;KJVtIKPk|g2!`WiwA93LOU>-Az{VghYyHUZOw z6JGBnibdP6R;#cq3#zJOWn~5X`};_z)4034!`0Olrl+UT)z#GwD;5E-_tL?orElie1+!(lQKxnL@_PhF4FvfO{pN+jW^S`A@m=Oc2t z_fXYA#A1Lf1D&0~^D|H?0r@;oC{z)Tf5-az7Yq*maONGdVJK8por#2Cu}BKRze)(Q zSR@k(K~>dh7)q1z3aVB^RMi4A8S?IqZ_?cznaPl_BK#c zwHldB+DxanD1-~t^(jW96&l9BftoT5G8(N=*QeZ{US2+9c=#PsDL|4SguvO^88l6U zWmyP?LXdv>xm*tUd>%pw3=9lFk|dzNA1IdtI6iiRvRr01>s+L18Wlz1+}s>hRprUa ziQ|urjd6Z{p3~FQj!%-zW=UBtyI`8*kY(q0ZQI7e!UFd8_TcyXadUG60BAHC=;-Kx zqA2L@?nb>{Z>^Lp1Ex9dfyV=Qy^cRKGlP1)j@8vwjEsyR9*+Y6Zf|c92n29?dJ5At z(bd(}f_c4w#{(BE3urVP|M2h-8yg##n3%xw@-jRg4=yh+addQq$;nAzZb!1wdM zeBbZ)0ZIIJY}-rI)R<16G8VgIBtp*2kdX)(i`_AuKBZ}Dv~BO7wH}giRj-F}c=!ny z7aySOQ;5d_MFISN;N=CVRDkPipirnIk@$h#-LIIM`svoYWXn>i>n4*)!E%`tf`63| zWVuWxlY+W#(z4V(;}uk|hpFoY=5l1K#W$(dB6B%X*9)xI!>=$QBxbV*Or;z)n{Na4 z)ohZfl*4THfI>*1X)aOIYP76>1NCKDq^8wqnoHi#z1|nh&b~)F4R|~dLZDPC!7vQi zwvBK&43EbH$8lg;7S7MlF)}iO@$vE3rcX=&)#^Lk+;~AnsWP8;FER{+s;Y8*eVw|l zvsf&WBuk|d#|Q)hczSvQ z0Nmf-V{~*BCnqP6Wf{d{aWGPf0yG*65@Z?h`CNZ_c^UKb^Vr_rMl>2lB9Q<9sH%!~ zyNygH1Ix0|>-7dOpAV2_NU&|7+jae`t1Im7?O}0o5t&Q|vMl5A@ex7@%+1Xq8jWId zauU0qZtu36Lone1}-)+I=n$0Fcp%DCjf4|AV&JOVW s{C%i%HslQ$>TV7(HU3kc4IycN0XZXHQ-$|Q?*IS*07*qoM6N<$g7uz8Z~y=R literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-2.png b/web/www/routino/icons/limit-2.png new file mode 100644 index 0000000000000000000000000000000000000000..b7344517dabd73e2746a1d83283a482ab052f7ce GIT binary patch literal 582 zcmV-M0=fN(P)0`R?U#&jCri zIldpIZ5LRt-!PYZWI9bA9+K%anae%0UcaGj7wG%Z?b--QY;au-S65$fd;1AO9N_d6 z&~;#M4|skCMkC8XM*>00U^ys?Ut4vCT&E_R*wScqP`$TJIGqP3-*lb=> zN(Hp-6BY_nI?lg|);JDXC`@VFC(*AjFW+!<^bz$s5Q*S#q?8y81~5$%zVAcRG(@&C z*xv`n;}5vMkAk{BX15!9scD+b=kqL=%M?Pe-|vUNuG=MbeXPLqj-l%aBM1UiDiw4( z9jK~`$z&3mgRTRfcN{}32E^l`nPRbs<#LHeqk&8&gHov!E{n&3SPTk$A6TtIGnQqc z)oLM|&B8DYBoc{m*=hy&J`^OA4;YO?Gt=o5K@h++O&Ep&%d)~{qY;oyK16tS_5-T= z8Q0f1^(BYy78uwu4Rk2e3vr USVh!xQvd(}07*qoM6N<$f;ejehX4Qo literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-20.0.png b/web/www/routino/icons/limit-20.0.png new file mode 100644 index 0000000000000000000000000000000000000000..9ed3ed168ba584ac4ecd4e8d7ace924f32b28e1e GIT binary patch literal 746 zcmV3F z1hbRij=E2mEo$Mm+FcQ&zrcYqD38$01T$?FwB{nr;6$P2oHL&m;YgRe=)&}Yck{se zEWUi-50dz4T(?1v6Vu$>V}(NR)!Qpwxgz!UN}FCKHuyJDQpCl+8+> zr~i!SN!hG4Gvmp&9c41nGsh26p(>n^63OZLbERIsR5TjZ z)YOzlM@O~3zOKp1NsW(>D;|$)Z*NbMw7a{jiHV7G{^5i4?3omaNJwX|lGqMpIK0J3Bi7IF3Ug5IE;=-oT9;P%2$FFbpsZ#>U1t{_=(S z`FREg2I%SOA)QXs($Yd>V+6GW-@tY8(bkqHo8{o+N2=8-sZ@$YBEjkD zDMLd;B$G+P;V{8qkmcoN`uqDC92_K>OcDx(7`b^9(rIXG%U{r)J5RWD>F%eM6-wnY zs#XE$?Ciua42s1f?d|RO{eBJ)59#XaqEe~U_(ju%d-ve;=cjf3XLY`Sy543TTjMwN cSsj}8JA{jCIH!(Y?f?J)07*qoM6N<$f-fL>ZvX%Q literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-20.png b/web/www/routino/icons/limit-20.png new file mode 100644 index 0000000000000000000000000000000000000000..675ab624e70d8746500cad33a7cfa126c9980440 GIT binary patch literal 683 zcmV;c0#yBpP)=3vvIT8_~(Yt#(NUT>nSV$jur|5zmRo3N=4C_Oxi4$N!#XMW!q%A zOeT{y6-A@2N4tz?kYNO=C?)3eWUIwDsnsI$c~VhIG>qUg%(eyQayv|=95$P819jDG zlBtx#TyBT9Er6O9XDn8yuKydTD_tjJu{t#^?*07q^aZoC?~zUe9uEM(wryNqUP4t> zSeAugFo;^MhU4R7^!4>&Y;5fJqfJZz)#^K(pL;=BuCiEk9j2-(qtPhW*4DVVxX8o9 zL$0o_a%E+Oi9~`oH#hF-ibYbEs{%}O0kVwOavTTi>+9Ix-^cLqFpiFn;P?AcC=@U_ zIEagj3)gg$>ABQN4Xfzra9UVn~fB)+X2DY|? zplJzgYyheX%*+7y_duruoSy@&mW|ELAK2UbibUe)Q~#6MveMKvlZAp{yG;tgR}q42 zx5+|5P}59WR(kAs6xnv1n$}{qN)Cs76T=}{t&*D7qHV_?WkSfTR1R1ydK?ViCK@vs zkj0|MO67n;$e?MiF_Y=jvi?mp#7J_=3g7_b8PBNdf=}A#ibV0bSSO zx-R1JI3!7eVHglXV0L!)_oL0v1KsXB7={m2)h_GxiOqCfXF8o`E|=r_`Z^86V58CC z($W%Zwc5+m)$63Hc4auu3RD%(@jMUtd>+Tg#|VeR==FM#Wf_X1;8j!=aGVuC{C*%1 zn5bp5S=`;-VP|It%gf8y+S)=i8pY}9DLl`6DGLMuzaKJO7Z{BuYRzU7`}_M?U0p@F zTt+Aq!e1GU0M~_#NaPZo&P1)>@5A#v=(>(_xr}DB2>_Uzn|oc{=>U<)rNr&+Z%~wv zI64Ak831r|a|6e5FpgR*7K5TFxW2wdFc?HQ9DaVm!0s+^d;5K=|7^+$m})ksI5qy$ dJe$I#{RLsEK=JfbNksqv002ovPDHLkV1ni!DklH{ literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-22.png b/web/www/routino/icons/limit-22.png new file mode 100644 index 0000000000000000000000000000000000000000..c94c15d7b7db50363fe294aede2e58a58ffccbe1 GIT binary patch literal 669 zcmV;O0%HA%P)EXyUj9+On+R3ec(2?j+L7DR$Ukx1lDQmIqX^_W(5$6L>Q~l2ylG-iBf3` zO`BtF4Wdz)nu6zNsMX-=3M!Qb@%RsRcfT?>_j6o7CY$DpXqqmGge}#oh;7SXW!oav zsz@SXi>B#fnkys53#icuh^AE}pBL$Nwwko;Yp(K+->2%%( z8mZF}NhXJq%N>estB9_zN-TCSrulE6kxWx07P}W+Uv(V}2A^46{6H!NiUNRb+ZcvH zu~@{iEc|}I!v_KZl-FElXW{1NJ=fPR*MXvZVQb4zC^UYQQmI5Xo2Auiad~-3v)Sb6 z=!l1h2hPvWi9{mI&(FUkD+=^_aB=ZYrQHq@jqvcyy(I~XrAvbO}cs!2S`uaLgPfuhr8J3oo*x%pBFbp!849m;Q#N%-%%;N#K z8x_lfe%}!*m&@$!?GX-#+1c5_Fbs0J9N}=7bUMxS^t2P!?}KHb;`7~7t2tuLW)l&i zSS*rGr#U-2LsiwWFPFo9s+u!hdKXP&cstQ1_*Q3>HF&qwGyYMg=494E? z_v7_?M;8p)EIdAbpXfiE@B~bBHYd0>{?k62z^45LbbB;W(fC?w00000NkvXXu0mjf DY0NEX literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-23.png b/web/www/routino/icons/limit-23.png new file mode 100644 index 0000000000000000000000000000000000000000..5f17e29d9db586493e578992dd27ff80253a8ed7 GIT binary patch literal 684 zcmV;d0#p5oP)UJ5?mB5Rcv8!H%@+wFhdz^RUnhO z3}Rq5Es|(EGrNHn^$8NBAiRsX3e_hN9imM8LuK82-&r(DcbG*3&I4!lJnwID&Us!$ z1lutTr)XMCGMTRuiJVC=C^9i25)6t&B4?7xd=*WLiD5YZoK-}GzM9Q(Ha9;}F3+Q? z0pf9pMqzju9v`7rgTq6pRGK6bKUi5Q5(xb4&-cl?J}s)MNiu0lwJKto@>iLrNVO`G zOq!yqn&|p;kMRO(HpfL(D^e(kbUN}Tbvh!2f{3bCq}d#QftjX5^7#!(r7UT;-v;Wb z-4;ouEXn6L#55g(9@hnVkGUCIgBBfN7c>A0JaH zl`sqgzu%9p>+J9E)9rQ%1Oh0(vy6_y$;o?;j-1YQMft+~yq{30|0v~hnOrW%#l;1C zdwW=xMX^|<(P*%CKwF9gcSwu?qGNKor6{@L^R54S(e4z+#K86+ju-4&d<+T zSXdyDNZ|2!czAfQ<%ve2)e5aq3Zim%s;Pag-l1_ia>;1^q7C0OLTwh<) zYPIN9&F}Z4C<>RCm-u`>yk76?3x-?{Zg0O2^q&p50R!#k0Jp|}+GhjUw7&qTqB6*X Sh?(^O0000nmG literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-24.png b/web/www/routino/icons/limit-24.png new file mode 100644 index 0000000000000000000000000000000000000000..82e88dc6d41bed6cc9c4bc45669618e50ce08d4c GIT binary patch literal 678 zcmV;X0$KfuP) zNsxMGFC9fJ;%d7U%;Z0CAOs|b$R!YsIEg5Ci)T(0F?e_14mQ@44i?f6yy^FTpZDXv z_xph)Ld)|aG>i<3#cifi$CPDqXo!?$GL<@JvA9jc$k6j5uX6>Gc*e4l*x2}ty}gf6 z)j>>60g3|j_5u$NK)nuBDnPYrVS4%}R#v`YaL|6ve>R(DnyP9rpZ8g-k-pEr;`?N+ zM&|QARn?$rraO)&k!2;Rs#TUsr0eocxGq^Lk*ZpyWhI|vzAsYK)>tS6Y_;Ad>dAKEzI?Y@zM^#mMaBx7! zahT0!nakzqzkZx@nN*a92*(+RqTnS#5MXX@4qIDWkR%DsW)th{>$tkQLQhZ6pQ<`2 z3g9^7BBD_s77N9)*(^p!N3pcDgt4(P+}zw?cXt;fBO^FFJHyGzNmv$(0nsQ#cplJh zhhmjV1*@y8n3$Nr!omXj`uZ?AISJdgVcRy&&(FiMb{p_Kh=|9JP_Kt#mzS3af&jX% zV{vg2{r&ye+1Y`nX_%dz#mvl1SXQqC@%WLz`T6gVq)*t~1Vj-4;JPlZudmT*wL~HT zNs@4PcZd7?dn6Kxmlq5yF9Wx?Kf3zQy4(R>?aeMujsG;yx-e;f0jG{tiJ|<9xBvhE M07*qoM6N<$g64WYM*si- literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-25.png b/web/www/routino/icons/limit-25.png new file mode 100644 index 0000000000000000000000000000000000000000..2c2c46bceac6469625373794f4c20b336f95cd8f GIT binary patch literal 695 zcmV;o0!aOdP)!z!N1cE}y6C??!i<1yY1qq2nMe*HtIs~mL9W10D-07U}$K~An z9YlntW7}@Atgx8ojs%0}5(tP44vGW+aUJ$+(m=sl(hng)PUij$KQ z(&;oxDRf8pEtlPb&X`h*&_cJ#3@+g^1hD0L4 z_4PG-dwckNKGxUQxw^WdTCEa^M9_7;(X453dked}?_5+WV}wGymg6|g&CRj3wS}(h z92^{QcXx-|?Isuu(%s$N)K4e`mCBe0j|bY?nrhK#l>7U8mY0_q9v)_La*~dY4n{{u z+1}n}U|@jR+1cNQwl?s1aADi<_}BzHJUnD|b(Qh)aTXUBQA!aA1elqbp{uKldcEF! zv&ToUZCrGAo>43|)k>ujj^mI{r&(HB;_~v6Y&MH&nv9H$5RFEgVZ|bJcAjanu<#9^ z?*p5g;BoNx5j_k dXD!&YzW_M}F2j1Q)@=X)002ovPDHLkV1nS`M{ocD literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-26.png b/web/www/routino/icons/limit-26.png new file mode 100644 index 0000000000000000000000000000000000000000..80b54de13e76c1b24283ef50153d3960127c1fd5 GIT binary patch literal 684 zcmV;d0#p5oP)mZ(c0 zn|Y6gy>w`)uI=9T5P0t*NI^6~sGt#a72Jb}MjaAo=DUYh*;)3`g8RUGdYw?x+sDHImEj8{;tri!j#OC}@IYRQ|_YKderBD#JpwVL`0vn-dS(+84B*wSpi z4b)Y$DUwLol1?9pWw{W;Sd?hADuu$ofx0RbM558E7{;P|ztj1|?Cg7zNl+93EX(5d z_Lf{OhiRIqs>=QSJ?H1=bUGbWRYm!gWpWbAWiMA(Zuh>Td}e)pf>5aUDEWMzR4PTi zUMHK);`8}9K0c;gE^~8pLnIPmbaeDJtSIpO4B6~E7mY@UaG2j^+cs-!Yn+^%;Pd%7 zJ3Hgy;ep}dVKhx+XlTfhCme=GBjm#4fq?;s7mLLR27~PE><|nFSz21c>-7?eM9Am! zR4NrGY+wL99$c6vwA&8v;^Kn6y*=jU=GfZWVrps%uh+}!>MCPnW3*Z=C#>BD)5L|} zU!hcTc-3kZ+qTK&a%^sHA|hy-#`g9$nx+wp#hkEG3H<(wA{!fD85#M&(Gj>@06acE z(r7g3R&8Qp0=L^uyp$yr1Nz#{K5mWww9opmX@3I!W+o>{ S1G1R_0000qC10 literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-27.png b/web/www/routino/icons/limit-27.png new file mode 100644 index 0000000000000000000000000000000000000000..25bbc400ea486fe39388f5db5d9e4575b873675e GIT binary patch literal 682 zcmV;b0#*HqP)V@f)b4$ir-%puUBMjOvLLI@%xJsjUI}o1x43wuX`;bLYs1Vn7zHvq|<9C z$_Swl1Om|01JBP;D8ShnWV2-!7k{#~^^K8{U+w<3IhUIeMbRW2HYA@HF%0=Dh9Q#A zi-g05D2gVz+)T^S6qU=vq9|EOCPivBc^9>sNHQs+C|N0&hnq6Puu3AaE0KsP)$031 zEvi+KNW_#xVpj~qifG!b1cN2X<^E07B9{{h21}x8v$ma=moNByABjf6VgbM~3@$G( zQB@VwG^y9?oSd9+b#+CtSfszdpTWVw=A{n}!S(eA&d+VO9gF2FYik~+rrM8^PN#{- zqczAfA zQmN1yHIK(by3I@_49H%J3;N5*YSjChM7Sada>3P1N;eFrd zi-^#5Y}+e_k(5kkPgM0>#>Yh_CPc=^MO5`%GMPOwjHKAM_s>~HM0ioNIm-6-CrYIS zG;NGj3gU6-?}vv6sMX-;2+HLqb8|mfUH!_~*w2^w7jmVN5KS{Aopz*J6>%K-s~kt9 zS`|sB9nmyHDwV`D;|bJkj*6z0rBD#Lxsf;N=0>DY5Ye=_GK z+d$8x6_I4JA%-#IU4MN1%;e+;G8s@50365R z^z@Wsv50NkghC;7T}Rh-9LHf~WCZ1Rmf>MIJA2RZvDdq%4(jYb34btx8$EH5t;jYhe?z9yT^GBq_tDwXPn)oL&_R99qa=^Me|M|O6=;{o9I z_7>ALd9GSG9L9BBEXyJk3K0kdUSBZea&UL|y{G@I#|`LdH+#4>{?k6|!KVEMzx6Yz TPX@0Y00000NkvXXu0mjf-Dy74 literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-29.png b/web/www/routino/icons/limit-29.png new file mode 100644 index 0000000000000000000000000000000000000000..a910f6fc81cfb3010b81c1596bb74b2788c75aa9 GIT binary patch literal 685 zcmV;e0#f~nP)_uSM5EyGz{?91i*RuP`Fw@>`5&yWe`Rp+XJ@}d*7Ye-RZU_sQwjwU)0Dr;G(`#p zkyy+WRnW9nrLDQIxXi`oDqN(sdCBNpym_{N+rbB)&~v_-eHyb(&;aR!#|vQhn&q$%QP)5F51fFB-_?sW!q9NCoL}8 zGEGa_?DR9^2~??sWSS>hUzcug^(x)oO6%*AX`ZN32|dAV+pV>=&q^j+y198B=vg;6 zQZm`n+S+H?wi{ViOwnjr+3df8o|Vl?(P&wg74v*-G(Isl_Lfu%TrOIz7H4N?WHK4* z^*Z5j7?;ZhK%r0|lgZH0(ZS&0;M1cAgHR~E;poVN45OgEJ?ElaE~j`ru7!mK#bPlX zA0JDSE-o%KKR>Uzxj7{g36)Bvm+toVB*Q4UsaB^j4Ci-!J|D?sl0+gwAP}Hds{vqH z7CxVk-Q8URfdHqcr!OOA7*MTFx$%0z=X3m#kr8HRXDJqoTwh<~^?CtlG#dE*etLU* z>FVm@;o*T_IsmU1H_ay0>yE#_zt6_T1}iHo^z`&lDwQag%k=g2QLR?7EQ?yL#_;g) z3#?v;W)nC4{YCP5$Df&*!Q=6;yu3^(6k>96lFiLc#>dB*n3y1)P7{elh{a+ruzVi+ z`-?6uEq$fC`#sy+;C2IWe}B)_)fEE+1Ni-ZE-x<$27@#j4ae{7?0f>XTClnbcX!{~ pI%jR(fVS>t8&l&y>3PzxwE{_m*me^8`yX(6`VN|w zLOu_uDiDbPPftLj0i2xy^}3Bh;XAgsKO>d;5!Q!h(_Esa87vlkS{CX1{3E_kS{7L> z`qVUorn&Uu=!QX#utzKU*_tW-!%tJAiVeVOlzESLAF>j68R*NI-}bVywf zST66;_eIb!a?ED0Xqx{fdcicwZ1#$Vk&`|?KYzmP>|2ydfFK|U0$f~N;Q06$uInO| zN8hqc!#Kc5DNF;z}^9^b>2~^c4tJQEL%d(iy=ef4F##}DP^Ye3(WUtrb z(a{m-=H_^KcsO{vYL!&grU=JbgsO%+D~bYL*HI`G5RFFh`1lyUx*!OM#bWpkRRtVp zQG_f5iW272)6-a4SwXAS!rk2+WLX9PjEsz6b#)c<^YaLTU{IzgfGk6V=K-!8<|ij7 z*xA{^=H@2G$H&obx6y95(d+f_S6mnHJcx+LTWB=Ge0g~pk|becV*|-#5(^6p*xlWQ z<2V3-$;nBKjg1Y;8Vw*GZwXvq|B6`bJ-&PaL=gb+@bG}!+gnUcO(7DA;QIO+i9`aj zEaT?p2BV{+h(@D*Q4j!ITfqJOx1s*CA$Pz~dvl0W<3G)_AxzqzmkUFN1hI^K00000 LNkvXXu0mjf(B3z0 literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-3.2.png b/web/www/routino/icons/limit-3.2.png new file mode 100644 index 0000000000000000000000000000000000000000..500cc94a6ed5ffe4f26dfbacf4f3210cb120be1d GIT binary patch literal 718 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 JPDHLkV1hEjHx&Q? literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-31.png b/web/www/routino/icons/limit-31.png new file mode 100644 index 0000000000000000000000000000000000000000..868746e184b9625a3f4d33e541ee03741cbbd566 GIT binary patch literal 643 zcmV-}0(||6P)xD3l&OSiIX_q=#Jm10tdjvKMy)iN8SH9&|}S1s8Yr?Lmx9m0}@%U`{XJGsDb$ zA4sBag`q;*&aqtHV>WxvOop7ECNmi_n>}Z_yhq#4F$|SIYbBEC;dv?S?0mxU@dp@2 z8m0;8IxsW@+}{JuCUANRG#VZXg&)}5{EBq?XRp3zcANzohRsq*&~-^6_$oq>u1l6m zf`(z!aTcB&k0Q@Y(J&gUR>^LcZ=%~Jt5wo48uYx>qf7{ymC80PD`KbfHqjHE4ry5t zE0t{uA%nKP#9Z!zj`MG#Cme^&}#kR$*=6h$~YJHz4OA%Y-4 zI-Q0jNvPNB5JF&VZ0z~bCMJMZ>mBNK1=RHxYqh@3T-T*(np{~~;qvk_PfkvFaB#rc z*;(%I@4q}Of07S`9-F*-Vm z%gamrl^_7Z5Hd8ajb^hiTU=a(q9|BfTf^k!Buvu;0E~~1BOZ^xlr@`xrnM!mu6{!@ z`4PLjfGqd&+uK`QUtdGhwBB7^U0q>#co>O9;#m|$z{Un}bMt+m|7^e=Fwou{;MDj} d^K1Z<_7}tgGw|rm|9AiZ002ovPDHLkV1k3I9?$>) literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-32.png b/web/www/routino/icons/limit-32.png new file mode 100644 index 0000000000000000000000000000000000000000..1019b1a903b116f04a0461f26b85be4c358516cb GIT binary patch literal 686 zcmV;f0#W^mP)f3=aNk_qWN0F(#U(OFV8%sU%|C@>kimNU0)X4?))r`IKsXiByEK2S^5 zsz@TylyrJsY}H(=^HF z^ITtFlg(zixw&CyXNPb&%+S!#OR=Iry$<>O2M3i(h)9G#<#M@5Bof48G5Y%YczAeV zc6OG{%}v~HH1ldp6K$-=?{0|NsDgF!6IqEINXxVXr{!9gdiTn3NlOp*EdAGqD0`TiXoj<#Q~ z*SWj9qrbl&+qS7xDzr||>-FMxyLlZwEHA_T{m-uMS(h)MtGC(3)c8+%)`g_~4M_Jc U8opbCumAu607*qoM6N<$g1A3GGynhq literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-33.png b/web/www/routino/icons/limit-33.png new file mode 100644 index 0000000000000000000000000000000000000000..3fbed3666537951f83f1b7b8aea2d8e8af8abbac GIT binary patch literal 651 zcmV;60(AX}P)LSshLM&+VONsLw#4Hiv$G=cxJWYDmO^1y3?nU$qrUD{L&jo{x+2Y{NG|7! zt{Y;Si!Y34kZn(kt~aDw73ufoP3reWs#OtPZ-{MAKf_!%AeG9t1*%sFgAcnCl>GZXj=D&enWSS!B^tBkqvby#3^ohB-4-^WZC;*H`BhJsyIXXIG zI2;mXw-n_wtE&+b3GY!ZE-omS%h1 zolb{?g98>87Kq2=&&7%Yg8>{Kz6;RpCP=0DUBO_Gd_GS$n`Lrx5)twFhldAWJ*gCQ zyNLjr2Em{&o0*wmWo3m{tHsUD4fFH!p1-@hL(?=~-42?DhU36+=*y0ekJ;bfXLECt ztE(&a_V!Q|g^i63+U+)-PR9=$4#9B<5DvGnEMK;?w1ldv6pKY7kqD(y2}MyT7K=*=quR1Qt|MO4uL?Y?F(BgNU#$7!HJV za)y+`aixWay?5Oe5iGQ^5D^5~ZQK>&2Ua2>yNHQdP{hUEeXj5lFXDj*xgWgEJMTO* z%)B!oiJs&8Q5r^;#o{{C=~GG)IW|U05}8h)vRGWFVPxt1(bv8LNp!L85v;C$#{T{s z6eWp#9*|{VU;uc01dfk^g9AXa&nThv$L$% z>mc>u$G|_7)<3fH)S$5o4jPFl&*JNKzQgo=N`%A4^~&dGC26N(cd6vGLvE$j?B&Za&;x*`|?-$zR1;; z$lRPShT%viGx^G>gFJ6Q4C7oF;!nSTSlDw7e3#~;LT657Vg%V);NKVsXUC;-%IH7+kN zIXF0=QmN3{*~$L?J{K1k+~40bG&Dpo7_1MyuMci+bk5E+Z9`GMFhAc*B+_`4>+5S) zR#xylkKNr}IyyQ?r_($>K2j(YNF);Ix?V3<6eyQrZ|{RjJ|7_#<4*+w0W8ZRnM|VV zI+38nEurf=cXxM;jEpcdGedWGH>Fak8CI!4wTenO zoW*sUys4=vG)-f1agqN1ek{x4`1lyxwwaiiz%C>RwkB`h>}n?|9(!h~cfT84?c zf*6=hiy4BJ&Fp3q?OQ|`LHJsvhDOjfc(c)zGDv*SGm9n}V-^iKAKcaVo!^CX?>UGF zJ;yZdqU%vfrS@cc`bq);k;zGsKtN=A`btu%J<;{3n5O-8ts)|HWf(rbd`XkbEuv|D z=I0?ChJgWidV*33PER49H;Bi7u)h8kzyD{qzDq6?W<=9;NhB;O7DX&e{wm87DHcT% z2}?9hmqKCYh4BnB44-IPUb0z{Mnm4DMnfc<716Z37>4f|W?44LWVR)l>`1--HqeXe zb&+JUBbm&$Se6aZ^*M<~Z>3Q9H_(d;1(9gUi;D}6 zj*e)xTKN5buCK2-JUpb`ZsYU$P=03_8-sG$#o3wNzNILiSzLTiDAawF%gamB=`@C6 zu)n{Ls;cbn?ouw7IX^!q5{WP}GV&Z&6lgZ#;NYE&S}jC4%%5^Pog|Y<;_*1c!^51M zoN#kxJImbz>uydlUG68h)!%tDf}X3>E2!Cn2%`4;!ya}W`_ zPP6G0(+o>8xg~)>S-f76@o^EaS0oT9OES47rWuxI)A_pBAtH3JtPwUgQXC)8V;G}E zqYw;&#{&-!P$211hXiQ(a4B9REiVv$HBg4^wWD=QR0*GmprT>OrveP(kL6s40l8Vzo4ZqRid zP1Cr(zNWvwA5GJ`MXeSrFT>s4kDmUs9#23|XS0V}<3H`Q9&Fm*mR%sH3AZ`)00000 LNkvXXu0mjf2B|l< literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-38.png b/web/www/routino/icons/limit-38.png new file mode 100644 index 0000000000000000000000000000000000000000..6369559e7bf3fa22aebb6fb75a848a8e2f6f5add GIT binary patch literal 685 zcmV;e0#f~nP)}A zBod&iFfag5Pf)GG$qAH72Fc`i*4B0ig?@DByX0~?CYq*8DrHNhB4XR}SJ}2mr6Q6_ z*`jH>l*_SK#tXFEz-GN32`bUGc*&(AqL zJfziX5ex%MQqz%xJ9c(CG@DQ;xE+{g1Xblvc|0D{=`_h?lHuWD?(gq$ zyWPZMF@}bQczk^9$)l=Znh^&s7kE59-sI#Y^Yio6YBlQhI;Lsj_xm|JJHs?h1_uXu zb33?PIB2(_)#~w%j*eJgUuSuF8K2L`!NCEls)$8% T8SR=F00000NkvXXu0mjf?Ak)C literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-39.png b/web/www/routino/icons/limit-39.png new file mode 100644 index 0000000000000000000000000000000000000000..84dd891f6cba439c01dfb8eed8a3e578513019e6 GIT binary patch literal 684 zcmV;d0#p5oP)v2aaoXBh*AMX{6^ zQVO!LjfKlvio4xbTG~la1j$%rGGgonVNz&FBq_{ea<&lTMz(Mv`@yaLbG}>Ldk!K( z&uOeeUk=I5|0Cetw>Tfq|D|n+@u9I6ijSDVM`Uqx^~6?Iw{(5R1j|`F!N_dHjAq z2L}f@oldTD0q9{B)J+ZN|fv)ScS}oq) z4o)X_+HGhwda`Ub%l7s*>+9?I{eDWN5>r!COixcUJUqIZ9IUOa5ex=dUS6izY;t~n&eGBnilX$&3Dgj(%qU;kO3C!nvh*~hK%pY~ZFHtlap-6fq? SijWil0000K~y-)wUj|fB4HHA|LTZ_wUiW6FhtQYY&uxXrOi`x?&b>F9)pNY zJO;}iJ+yRh_qKr@y>^hcd1q*Qz=qab2NdNr2Di*t6Lzve_&qCMHlU7IA)l4gfGs6MnxR{r&w21OjL@8XcI=2S^em zSQgN1I(A8tFf}!WtE(%NN+n!eTp$n#AQ%jyQmH@)fyc*3jE;_WV9h3AS$Na4R;xvl z+}qpZ>gp};vcf{4z_qnC&d$zqb90mRdi_n$TwGrMfxf=a*xm-*ZYSCO{XH6u z1_lNOaC>`;P$&e`G*K>>F*rDgo}QkzW2*(MuLJe^&#ul{mp7oRyV=F7@t^Kl7cT8@ X=zxE`oo;*200000NkvXXu0mjf&0abP literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-4.1.png b/web/www/routino/icons/limit-4.1.png new file mode 100644 index 0000000000000000000000000000000000000000..4e8d0054d1a43ddae3f82c086e4ba17921486d34 GIT binary patch literal 666 zcmV;L0%iS)P)p+g0~6=X*&^$(*$;3bd*C(az5BbD z_ujoAiJs;A5n5K7`TQPJsS~OyIXOwHDw#^1FrVL}Wu@u+kzZpak_hEEJn9T}StE3S8ql6%< zRWh3u)OCxtz3{^5f*fa@x?W+SK(<FFuX&(EQ% zDx{zMa=8p41mf{{*GMLTdi@Q`IW#?{po&d$yN0E2^r$Ye5@ot;Gx1ii3m6i^h%@O_}&4t+&YFh4&J*LC5# zE-o%EaCv!&PN##vs@(>BA2Je&W7KM)zrMZ>(==fi2G-Wru(-I0?d@%No(BLJ8ymyO z$Ve}&Rs#}=V~HCZUl5DE$L=m5%i;e0{XHHZ9}$noadUHnWHJdwQP5~KFg!er(b3WF z!5{!OH-WpmuYLVzeeQt1_GTZa#($b;eVDZ00f-|@JiNzylmGw#07*qoM6N<$f}4pq A=>Px# literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-4.2.png b/web/www/routino/icons/limit-4.2.png new file mode 100644 index 0000000000000000000000000000000000000000..d90c7e3130b394e7802edf028a9bae41440e8945 GIT binary patch literal 714 zcmV;*0yX`KP)T zY1O?rW@r&t+pU9ZTq8n3e1eSu8$}!>6e1W!Au5r+_r4C+NJ|F`=?8Z?@cqu^-g7=A z@!N46zid0Mbb3X@!$n0R($l9>Bq9wD7nM%0$hPBh9RHtQLlSkB%VFNXf5XPcG?vxP z^XFii(9#068WalfySviV zlq<`!mCwiS8F!#^IV{WC(!zptd8tR~@={t@kSuFU<#PBA=DI%3&1Ez*IE2Gt48wq~F4)_9!sezQndY8WSAT0(sZ^9mB$P^}(=kB>PxH~=7#NH8!k!0hZS{r&wU zlgS5Ib29`21aKU+fdC^TBa})dPEJlZIy$1Qt&L)_$o%{~)6>(8jg8UT+WG*y zy#>d?|E{-pjA*ozXU_l_92{hPe4O_7c7nkm6B83GE-q55)hHASoSvStwzfu3PY=;( z^bWSZ4qv~1GxYN1M?#@jEG>c0S8sN8b;Zri4ILdFR4NsM!64OY70>ek=b%7 literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-4.3.png b/web/www/routino/icons/limit-4.3.png new file mode 100644 index 0000000000000000000000000000000000000000..73f0ba5e87265a8cedf97a9620a1d04179d0029e GIT binary patch literal 695 zcmV;o0!aOdP)85(X+dhE0URTwMJdA_8|H@z^Cc z5jrk=^w841-P<1Y=+#3Ilmp>RU>4CKB2y?$pmeo*+YAOPlx1>iij-wC7_2an*ru+BXj$Gra|M!cRjW;4bMrHHc0NH> z1BgTbMFEC~fldcFJOp-kfnu?SSnMa(*S{eUsJrdLYFj*e*?Y9Fvw8olDZ!DetCZWis|W(NF)G3aC>=te2ml6Q^>Lm zK@i|L4oamG3WWl?-7W%w00cn*{C=QZ{(!wbFQ_PGX0xw~-Q3(T8jW&sagp}#k1$P> zkw}C~OG^xg!`$EB?8>mz&xVpOP!@6C- zvhb#7j^mIdbGaN>S65lBR+&sDd2nz*P1Crru)yWzWmc=zH$4-$vhp21UlLnefXCw& zyT8B3Q}ceGR|gk4~q9Mx%l8@p1TkzTQR00oK-lySpC)owEUNz(99%fUfbM d{A>VD`wIi$gs#a`)=U5Z002ovPDHLkV1g8QJ^cUx literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-4.4.png b/web/www/routino/icons/limit-4.4.png new file mode 100644 index 0000000000000000000000000000000000000000..d0240356cf7dadf9376644348af4c508d28ca8b2 GIT binary patch literal 603 zcmV-h0;K(kP)Br+1YWjcL9O^ea8ynn`eNWzt2gm8BD373~2peWlo zJOpGJ*xUpj9)O!0;OYvfR16#){lMwzS8Q*aZoKQP>-$s`jmf0VYL&EYJ}cWMt5q_Y zw5cc>b$x&3u{1J_5EZ4uLV=u2_$EyzWT8MRN`;0ITAJCmzQRLbFa{5GSN#$z&- za+uE-Y1;y*X$OqO?x^ekX0(#7ld;$xHSNIr`SI}!c6Z++od!G}x0dyK9nEGFk|e?N z8V?46TJ0UKuf3ow*H|h&Nj4Y^7>~!9NF-?g_7N{}mP({7*9444dyr)}^K3Rlu~@`# zIE2sVb6@om2e53P(P&SAC;}TBE>~3*s;VL!4x`;}qtR%*$^k6nMG*ol3s@{%E{Y+wOVMm+i)BQvMjs2)9L&&TPy&}!e2FW9ET)XE|;0h<=E|ZnaO0>>-Aio z&1RpinTN;6->|jy5$ETCAh^k<(<$cjIsAS<`u#qF!5~CYba@~USYC7-;N%3jzyH40 pK3nqytaUcmxHbOMK3l`4{RssQkASqyPkaCX002ovPDHLkV1iCE7WM!D literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-4.5.png b/web/www/routino/icons/limit-4.5.png new file mode 100644 index 0000000000000000000000000000000000000000..bbbc15e50a8ddfe7308891706509d2dedf6ccc06 GIT binary patch literal 700 zcmV;t0z>_YP)eCK@S8qsH~t^%DNU2xA9^j7B+%iE?_u2 z1?3Fsk1JDnSnFMPMJ$7bohV2ai;WOe5(|Y$0-7a({K4SvzAMxXo>zE~d+;^G%x{MI zW(Fkj>RFbDx*lL8vP(rdr!12rBcv>oigM0KWS6=gpk;afj1@@2RiQ9|t*tLOJp2Sr z8^p{EpsIk+2Ru9gXJ^3iF_20XFgyDTo15P;I9PP!U9w?JQqy#XLN?QB(zf|m**2L@ zlcA7JP19)@lP`=XP@ynDO-nHrBP$iYOO*;4i;Nw0aZ1_MB-Jl`}=zagF!AWEzy4d zh-@}XMNzo8xX87&HRkjARzM;_s%l1rX^ulx-HPhZfN744kR-tCb?wPy63Ju|V`F2uzP`rggV@>G z!O_uC3+D9#k^~W!1vHzkU6LeBOiZ9yETUK};_B)OH#avhO%ubz!{s?}-> z)@%Zng*QEO9ET*ix3|aD)m7f!-g0GSg$D-*Tv%A(^z<~>*VkDpmEQDB;PUbhba#Ko z_BJ4jZn9dfhDM`-o}M1u-QB_O_oH5~qf)7$zrP=yot;mPjst9L0M+Wxw$53bH=wP% i*~Y8!pYB;3F70ph&V)uD1CI*;0000RXw(%4!1q-cm0Yl1w z$QjZfSElf=_w25SWg5$XAX%G@5M2o(D#`|2Ljrza+>dvKy20}b4{{Iw&A|L-c>kFJ zNxXTs?Wd~7m`rXm8a<*Uk;B8JB$3hR5tGSHs%nh3?f)~^M-rY4BaHR+uP7G3Kv9M; zJq^e*(A^C@J^}{^!0s+kDjAra{e`u)9~c^{dHEh$)5fVND&uj7$Jvstp)YUfYBb11 z!eu6tqvHsms!lQ%yQHT58|YPCdwgnXWq<%)oMeH60nb##A!k6bQ?o0}VSb#-}N?eFiSTCJkDw-=E}1U)@H z?U9mYpk5ypAc{aB;Kd7t0t$r!B9REr&dzXrd<+1vEDOP45EByD!2JUnc}0s%l2 zA;7kQR?CZvqKL7vG1O``)M_=HoSfj|;sSv{0R8>_n46o!z`y{S&1M_cY5}&5cb>Vf zOOo8)-ex+T=GD~|Q>henU1vI-=G4>_S5{VNn&vytd|Y1siN3xRHZ}l3@QU5t-QnTk z0l{Dp*Voqwg+h3GdV*=17#tjId*-^p>MC%1yVcP->+lA2bT>PAHU85*>%gV`4ZF^P UuO~_;5&!@I07*qoM6N<$f^C~U{{R30 literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-4.7.png b/web/www/routino/icons/limit-4.7.png new file mode 100644 index 0000000000000000000000000000000000000000..2e0ad010d738181fd1b2eedfe4adf667dbaef92a GIT binary patch literal 672 zcmV;R0$=@!P)V2}j&?_8l-FXt5=A@ zMB8!QAPpnVbb5=?Xqk#a4h@ltLPn!yrqf$Aj5u94_$OB)2_LrIkM;Eo4i1t~)d5UR z0kIhH;RDcU0LRC`(GgH6*qE95iPhC_7#MJTzOQVW6I4}$iG*OWND9HfLI|>0Bohfi zRW)du6D^HrA=~b!surm0WUa<`p;jYxomABVZM*+jOo#wAZH=jv$7=O`LM>FQWGdxR z)7B_N05ptA#^YBs&3_YW!8FNu{ECJ#8T|6}^cBOypO8)ilH~XD^z;;#WkFFCNRos~ zrGnkvU7Vbppjyk4(U*L9AKjd6Z{o}TBmCEDEFWG0hgqtSQ` zS{A7&3_zBFP{=PYm&+lS%VA_>1eHn!XJ=>r-R0#a4i68pu&{v8Zyba|fGi^b*9DqQ zKQ7BM#>dCuI1U`g!TI?)E-o%0guwp(K6-k3FgiMlHZ+@n>*7t%T4n9*>~MK`nb+6X zTv}S<&CLxpP2=|V_S=cR^i1O7;&+6@pRut41Ok4tdcBUv$45jW5!~M1qPMpf_xJbc z>gqx`9DaS3=K(7#z{A6jj{dU_Z$L+PvxBbjpZu%?PWubTgs%h2LN)RL0000$~wpZ literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-4.8.png b/web/www/routino/icons/limit-4.8.png new file mode 100644 index 0000000000000000000000000000000000000000..6fdb029b46f3aa326805f3914dd8b95802b80cb1 GIT binary patch literal 698 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 M07*qoM6N<$f=m84kN^Mx literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-43.png b/web/www/routino/icons/limit-43.png new file mode 100644 index 0000000000000000000000000000000000000000..c7b9e27e807b9c6d861a00cd109ffd0658c8d21a GIT binary patch literal 655 zcmV;A0&x9_P)&~KgbL?@p<@=Q zA~IP3m^ZLV=X!Dou0Z8Roh{=JQ2rnn%le8)zWQA~nrp zK3}Bk20>jF$4zQ@o}JDe}{vE5GYA?mP&sWyt%pI!omWVmX>&Qbi_<1!^Oo#rqgL2 z9v=G5r4lJg^&mQ(8Ay_^qx<`N6pKZ)+ieIT5Q#*fX&Mwo!N|x69v&Wien=9~>C6Nn z1P~7UV&!rf<#HK`L;~mM=jiwQP!t6xCnvbQy@e3M4-1C@At2y5fNlF?LI}*x&f?bB|~7QP*KPzk<855BIxcUE@Z(y@QTCbe(!SMyB8!8 zIIbI_u4kxeS4<}FDa+*E9x2OYGI>u;yP~dV=(?fTxdKV7WLa@sTztm$^$ApU7e_~c zq5xZ4z|#|OcL&_u0IimVGgK-S zj7B3wA`v)_v-06^_;*!HDGD$iABd17AQ}zC8jS`TjRq2l1WeOJwOU29*+e3dz+f;y zuh$F0qESGSAi{Nl#Uc=sBnhch3bt*-vMh{7Bc#)5*tQMZwqcrP5VlwVu8Xxj^L?Kr zS+CbwEEegyZovPoX97=8zhis*6D}_SQ4Cz@I1c9XIbyNcS`Ps5ecDGbLMkacal>K-sRgNXnUoDh4c%*>3!elH@x@0 zAc?^9JdwJdq^9jsmd_cFlVf9KJWk5;IW=vUx}K!xiGTJAB=IWK9K!bYM;sn5Lsf?{ zI}0cZ5RC#)Pe7#t93KOvl8O2GAK2Xdg5lx%YyT_RFs7)gI@4*NoYiMh9e?LsZogb2-v+_$E0Hnah!?TB2zVbzr_9Vm6y+Cex;EzYX*v+a@!a zHnZ70eLn>1dWy;94GrVpKrb>3GMT)guBXJU=jTrtAAb)`0|WuT>*C@9mzS65>+8e8 z!2wQBPf@GYFfuZNXf)a}4Gsd;Y78eQqPQgppRv3wA(04xEX#tXX*fGO!}Rnt*4EZ= zcXtQdwvkGu5R1h+MnM2v7uer_7eb?vfT9FI4-XH>=ku^E3z0|!wOS3X>p~Po$g+%q zfq{S@iUKqmi4ekJAQB1Gip3&|#UdsqCUAXyjoaH>EG{l$Zf*`cJ3BZ!Its!f5g;5! z2%ZPDT7gnQ=WApX@3G%q*M|;0!y3# O00006pRdZrwV>F^gkBketg!i3TzR1>rIz#~oc={S5-jziY# zWG3TKQB-Q$>WJ|I()AD(rN&~Bv@E_$mPHneq@vWQ>!BBz;|MGiO3Y?mn&$gJBbg?d z&AKcUN^~3nRMmCHV)xXve*=xAX=E&RPgPy_e;p3LVQJ|jayh`~!|!S~n`pIKSXfv< zv)RPy=_!W8Awr=Ld?WAN9MEWd!1=i!l;sA?<-ZDcyIm#{38vF&-rnAFYio+5STOp*XmgaF$H1_Mt_6h*A8 ztf15BK-YDcritn4X(W?L1cO0XmgR*F27qnj&6&BbOOiY}IblAZr(qb(=ktum7^(&d#FW@1xi2VP_V$2>hvTvS ovoUYLSa)-bSK~k3voT!SUtqv{sq`|mnE(I)07*qoM6N<$f+>MA3jhEB literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-47.png b/web/www/routino/icons/limit-47.png new file mode 100644 index 0000000000000000000000000000000000000000..90cd65fd01bde3ab412f02d9736003d8c10aeffb GIT binary patch literal 644 zcmV-~0(pOOVuU&6rcFIEw?gxIS_ulvY-uwOD z14%fJ5FTn;gz5AVgTWeQnOs;PWtj{HYfPt)sA&-j;dvYDB8gW_a}N9a8JwLZp(p{Y zuLI#QFfjr2dcf5caDEPy%O*B9ej%IvjzFOG8vm-S>#I~0jfsTCN`NLaN`*`$ zEGmjdU0)q)41`Q`j*3#Is*;@!KSZZPsw$}{Wt!&PK+Lk-%;ol&O4)3;KPEJ!-6m5h zo4MQ`Ez1pR+8QH~JL>ws2@TP8G7`C?rmcCtzPx6%_v$H_8`U#hp9xxoPGM|5!@bU4H(P)&hSd14J7fdFT+}zyc z{QNwN#iG+WpC`lNDk#gOVK{8vZkMX6a%E+O@pzoJZIdJq4-c8iWZ3KVoMMJS$}-)M zB*5!+lvgMeP$(3zw6ugqqXEM(&}=qQDwVLcwFU1Re|WusBte1@K)>&>Ns@$6D1>IS ziB_wH+uK{*-``_qW(Ld4%Xmk>4+w#gJsXyFa&p3*ogE4xn9XK+e0)6mMgR27#qI4M z@cF*r-~e#D9SOVLE}oyC;rILT@bG}i$w~NpKIg4$8`#|io}PY=^`DJ-0>(O4cwDbd8TVIh#v|jRG$d;9+rkTv;BpVG&7$wq_B z!Dq-CXhjAxK-Pf*hgmP(}S@=bDGvQ#29&7f^hJj0|^SS*&A&--k*-v;Wb-6r#S zpT%OCQYxTn&NGv_q-FgZs3*%JGnq@8=6v|`)6*ABO}$5<0E9yLU8kp~I6FH-B9TC~ zT1Bl^gOn2E2w-{gM;XFIsrd~032sl zfvN)0XdtHRI&@vf^z<~&&(C2P28M@+addP9$8ivg#e%SC6i`(t@I0W~4a8Jc#oXK+ zTCEmr+lH>|5JF&kdmHh192XZCL0Gp7cphGzneY1~$^HF(ZftDOFbpm)FEg9Xa(#WB zS65fB&Md^WwQm?2`iR|KKv4n(@9yr<>2xqUIttfy;kqtHMn({i$A6i8AK2UkZg0Q$ q^`G^51Nyp~eY_g~>7MoB(*6YGt$HQ~0j5p>0000P)`AG)>Q+xh|4;k*<$pd;1HHjy^$_ zgNQ}}NdiO>czguT&w=A(pj6Vay!;cJo8J)()?V^o$f~+PSyq@#TGTYsviPPfi_|nS znY1X&3RQLCnehbD^>NB_iMbrvYVloawa8qKl;sk2ef$Y#S#D;td8Se}4dZ>FXBh^W zO4-b2^Rz5Cs3?n!M5dNDdW`f3^*1IpzOI63ivl2m4)@K?clz0O!H#zZ2)v$Hcsqfy4=ajvYa zuu`cw&4mIfNo6;h%{fStqoYozgM2=ZMx%kDp&?vdU14No1iQPt5Cj3Ir>71-BnfCX z=iCqkz~^(sip3&|#Uf^AW>BeAAc`XH@9(j`z7ADY(eL-2FrN<)1PCxqpx1N61VO<3 z{5)=NZ*g~bhibKo+1Xk6{eIls++cEY(h2MJ0Mo>qGqY`*BzbUfz)U8?TCK*_)m1Jn zEpcmWi=9sA&6&BlwziMq;m_FF0o-m!!H0(jbh}*y0s*w!Z5W1uKp+576kkoY4Qy-x t?e>p>{<8sZz(99%fLG%`-LnB)+FyYcdV*|z94G()002ovPDHLkV1i7cE3yCp literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-5.0.png b/web/www/routino/icons/limit-5.0.png new file mode 100644 index 0000000000000000000000000000000000000000..a135d281c8ee15120acfb5aa1f01aac51b2930cf GIT binary patch literal 713 zcmV;)0yh1LP)qhl3QsBauin++E%d!3!-NETk{|%M0K0fBgUV zy&#F-j%|CXX>qFRAtmXW(I`1POGcxlBwbTg52I$ zOks5ukYzv=ftMGcQUQuZpj4_Onf!rl_A92Qe!BH8S=U#nC>qmghvhQqIQ**|hb))L zblRb!Xw>zUS4Iz1uTN4@O3dfUW|MDHvq|Rjq@t8quTS=1jw3La+hZovWux&n(5o5^ zGLz{tm)oP`2%x4V7>^s&^?w7sO4rGF+@Pi*p{jt#gLb=(-{Jh+3(9hpg@Sw0&CLxZN#f?_Cbze@ zX&44c^7i(Y8yg!;rBX~Llf1jT>vvZukg{AAV46#iW%qY$wHltDp5XO*AxVrgYX__z$17l-jFijJhrs4Vd8L?Qb z59@RQ+lGKp=n9pJ>&Id-6pKZus)~h$1uQQwV|RBK^YimqTwFvpn}sAvNF)+{Sfv7l zLRTJcZGFSY$Vcq&1A+hmXf~U8e0;>j#030)Kko1E5s5@#Sr#519uN+PF*G#P19iK= v&JNIOeIMwY4R`|vx|;(`jsKKq14!Cmnz>P32MQ$!00000NkvXXu0mjf?-DuF literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-5.1.png b/web/www/routino/icons/limit-5.1.png new file mode 100644 index 0000000000000000000000000000000000000000..94a49a06b3f5a6545a9da384f3f56590d68d4e5a GIT binary patch literal 680 zcmV;Z0$2TsP)G_84jST+zzwlFh<|`v8u08t zAT?x{;?%+7tnJp#!NFZT2ss=s3F?x?K}iug5=jtaa`|=$Ua>#GLi)hFd|uvXc)vdo z5nh~D%Pocx6HPmiNaRw&VUejRk#JZf61fyjI}pQ&Nvq}l^{q=pa445Y+1>rX$;mrZ zb&SPDh(^KZgU3fG6hPM@mn*Zp{Ee-x&y0f})tqE9BGszAifUCPlMzwXoRrI>9hq$_l1_h;RH`kN%IieW zR4O8=R9n*NPh#5&Vi<9W#jeFP|4sCaX^O;R*J2oP_ja@Sk?HBTXd1X&JUl#be0UuiU;)j}Z{yr58U^2y0bbX`Z&G-hUI zn4h0#XJ-e?vH%zy9HhU$|D~)@fMD>#CF|>72n618nbMZ7`} zcgdhoZq>b9H-}z|qqbWIcQ+3d1I43A60jZ2CV^ZKf{{q5^qlkDp_tIpp@sB;cX{Cb z{r~X3?}H@1DbI_@bu+T?(#lGp_4RG}ei*s#V`VZG6$-xwx>unfWil1H?&HYIFJJ!Q;lrocHiSYn zn@tW54mdqMrCcsEGBQFe7Q^>_&d$zo90$+yNG6knLLnF$g7fn~`0ycuO!Hj(`>lsA zE-qvkh87kUw7R;gN~I!6avVqLbXtpxi?S?BCnqQE-|g>9rg+55FexB*+X?Aya znV6VhZf=g5nHieRCTnYJOioUc&1T!Mo*sxs3FCQich~Y8jRs?5W6aLZGB7YetyZH_ zsc?9B$i~J7sZ@$wE=O-~ZyR=Z2cAcm{{D}YN-ckUe4Knfk8RsbO-+$XrP$iqqF5{f z;5ZJsT#looqc*Hmg8u%GAw7HchIsrhUcZKL7=U`c&dtpYi9~{~t}d>wuITRWrqO5+ z1OWhvM1puc4nY9VpF^YZwxfI2;S1>KZFVp(!4$!0B%%q;$G>D+3S%+Pqo@OZ{PmO zmoG1{tT^d3m?nfm&}u=c1o=E19@fZaKlA$aJL2*0z5X7#Prm_KQlp{YQllZQuS=G7s9J6O9_G4Ut*yOLF4xu7 z)$f5Gbaf@=a$T*hy^-sBk>jM4OjcDW{5Q~p3I!>dtjcjxzQ2C__?stBo@3kK@o;;4 z%g)XYM@L6gDitOsCJ2Q>bh}-SkB`~g+oRoX6OYI7csvk`!O6)ZzJB!~(>&4kcJHCn z(^DCSp@oG7t*)-BTCGZwip8SR>9iIX7nMq-bZ~Ic|K0YsWSS>l>h-6XW^cNcN`;%7 z8+<+=hG8%=G6Fy_7$ld=kG@)L9>c#JeV6f+h!(ry<=gDL;Y;A3^ zzrPQ_^z<~DOono~%;n`Je!ssD3kJdO$4jRJ?RL*^Hk%|83FhYJ7#<#`UawQFR@vR% zWn*K5m6a8ShK4vhJL|*RZRm9H5{;HAm3scv)D-!A9^1B=nVDgBc9zY}O%jO&fk1%e zdk#z?2reORdk(P-JDmoGmOiTuI)_u%yc&}cNczP@I3bd*3K!1?((u~>{&tHs5| z1<`1fNF;I(>UQDPD`+-94Rp^2d;tT!%>kyy|CDC~NZOywL{ly{x%No_0000u8?< literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-5.4.png b/web/www/routino/icons/limit-5.4.png new file mode 100644 index 0000000000000000000000000000000000000000..7d841881cfad3bb8fce8999e91b8d40cf3350f8f GIT binary patch literal 695 zcmV;o0!aOdP)-8aEaqc7k0tU`Qoc zgf*mJwoKt-?e2D4#4^~6f@H9mD1%DUNC?g#8Zwd?49?8iB4pej3m38nUUMJ!yO;Cc zy&#F-j%|xn)fiK$uau+MK<>M%xzu%oRw&lVOZuXXhgh4?jRr z#<92v$TAQL0Z&iB*%@$r43tU+mX>~Cd;1H<$4xKaBWv0m6-8wt;jmmL9fz;VamaF+ zOe7pCib_qJ>oa;F!x*EYl$gtrmc=*8vdCPHRFo18W2^^r93QjUJd;V6&F0%ceKnh8 zGU+m#&C_vwpsLO@7OPX!{teWZrjfB&ovJ!7etLfXjOpq3NTmQlK&R8e!NCDePft;; zRxvR#flw&qd3$Zcg({T_B}wAi+8VdEwpg#% zy}P@+JI3R2uCK4t`SpPc1yYuE9~zBW$g)?tTCJkpZbKABNRoun(NO@vx1791cP3DXlMv4D=Ub{Bc-{0rv=H{!O3EbHDhT-8fc6R}v4*+0U z79JiR5DteC2n29@dy7aU0>9sncDs#Er-N`f-0RA9flLNywZ0GZpAC2d20EJq+#3IB dpABHs{shD6hd^<}uEPKT002ovPDHLkV1g;xIamMy literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-5.5.png b/web/www/routino/icons/limit-5.5.png new file mode 100644 index 0000000000000000000000000000000000000000..fa228bec197e98510c05c540b77e706a33bf85b2 GIT binary patch literal 627 zcmV-(0*w8MP)PGS&p(JG@BwpAb(8|h%}ob<#HgJ zW{7DnJ~{d#%bFBTYe=mo;=1y#xUNX8CZcH#v8>6yEC>{-R(GXR>51dKPxKVW5vf#q zQmyVv5GaUY6s1tO5!3uP(Nj!Qq)@mK!ze~%W8*6$BOln`2SwrG;eq4hWA5(mNF)-h zuC6jRHufr~*MqGs@Vsx)Pm#!H*4I_CSpcrCuGrk%q}^`gI1a^PkXgbUJW! z6j!kAEctvm-OI~MI-L&DXp~$oM>?H;lLP(rvF)rvECxeEAx|cgtgWq4DwQ}mIN^{qkb9oT^z<|0x$iEP)+z=^u+|Ysdg=hJ~^BJBWUqpo8 zs@b%Pu7^d_4kQq`60cWeVnW306$u2cMAHsL*Td3mTL0`-M1+=#MGw2XpGYQ`QPmOV z<{%gZyB(gMAe)6$3eL`o%+LQ|bMq@BBR^aHEpjF^E2^qXEN05Zg@|d&UuBvi7Z)P2 zm?^5NOC~e>#&`u4iyl$cGf5;w3`5=}!w^X%L{#-mibc;W%rq?$kMGFJ%8OJg?*qN5 zQW05Md69U0M@-X#=z2uL;euo`{|0(fCLF zPN`I)QmGJ$MCj}5T`L?S^hmm{4{6AFcJI-Q^>P^-b=VYh{Hd75CbwcUI^PpwwN zYPAvw1aP@r0Ho7tZfetLR(+9wqZLb*I`p{omQwwCX3I9OU*A{vddzrWAP z$q4|BMgynQNhlN|nM{()<=QZt4Z6Cp&}>4z-tw!}Dq~|~EG{lGG&DrHT&7ScV6)li z@9$?}VS#~x0Sv=v!|HWtHnA``m?N8Q`Qzi`q*5s~O=EI$l9`zqwzs#LnwrAr^Rc$J zhTrce8jZGL*(?kW<`h|7{YG!^NA~u>VgbM~3?3dH7#<#`yStlGsf63@=K1-V`}=!b zE?0YIFE6mM0oCgFj^0^^KcJ((*}>HKPkGjXr2PdroiS>Uh17Zg0000`4Ov literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-5.7.png b/web/www/routino/icons/limit-5.7.png new file mode 100644 index 0000000000000000000000000000000000000000..db7a31b3d447800ef46bd46df0a738d806d2db2a GIT binary patch literal 692 zcmV;l0!#ggP)5?d(U3q~!f|KLc;{DGLl zMY|TQS~LtTYi2jC+ZIH4TX@ee7w)*J?BM4 zc=wu3rx-?95{W$t2G1oB5E&j82?Ruf!E;F@_QWv4(rh|^_c}xbTZKYD+uL6_Ir)UD z4lpwVp%8R*z|#|Cv!LscP8XP)`^oC+R|W=(w%;b3=9H+aA+eYxnT&{K$zNqzBAJXx zEM|$S8e*DLFN|kUq0ld?nwDfzq*|2^sah3DCPh>=Ermk=Gt9CSSzFtZc)TT*%Ev%2 zs#HYc@s_NuZHZ+mh+#w|9KI3L{5Q~xOj9Hrz7fNSIAnSGJ6`W+zI_8l;qmd2!^1-! z9v

  • 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 literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-53.png b/web/www/routino/icons/limit-53.png new file mode 100644 index 0000000000000000000000000000000000000000..cd540638549d9dc1931b72d2481c76f84af379ee GIT binary patch literal 692 zcmV;l0!#ggP)1GCqDPe!s}bh=|`WGCqDPnzk*v9u~u}{yD3N2yK+zi$Yjbyqu*Fr`Hau^y*=M1XR{Ncs=CBtrsQ%WrYV1wX^P}> zBC(h$s;WyiJMqGJ29?WRQPqs3QX;jQyh^p2NGc_wsu?Mlz0WYyv`8|!A@O)is@2zl zUR14$#N#bVCO5=1Er_m9N;rHe+3df8UX;y>gu|Dj>yy^Cr>75$j=n?FKv4j=zrSaH zf1l&yV~WKhUayz4voj734rn%;_BSY^>s?65<5FP1cO2Re*be=QJ~R)-Q719DwP1C5WlNfEYfH+uv)DI zgFyxd2Z_aEM59q04hIhp4;^_zA*fUW7Hl@K+dDjm!@=zAEK^fcY;A3EadE-)^fZM+ zft#BfY&Kgb%x(vp4GV?=&1Q#Juh$tG8e(Q<29L+X-rgRotE((7F4EuMPpMSugf*LB z7+7$*3gq)0-th1+>2w-R(-<2YBauh|u&}Vez`y{JNTd^%&x6ZVP-K4o3r^>IHaEdy zY5TQWjoaH>+-^60eSI_<4X&@Rak*SLoz7pNRtuJwpt{F|vXd7D3jrh`5cXg@~PCmkSuK z5(MQ8>5uEQ@US=6T@h=0LC`EIHcC)Q8WC}~Fd-`mA;I1JS9pmR@xX)J2UE?wZys;v zJ&;7#avX`e9$`FwKvlh@qL5=_q@s|jddYbFfVv)`<4CV#JtWb=G>5Ub_XWqtpP*?W z%*_Ep0DeEvXaMKuz{v@a$(UGJ_=)Z9?+AsgPJG8~7}L}=ozbYvT#j^IzKQFSxf~gd zy3{nChB4iCJc~?on3|ShGD+Gt--T_H$t0<18Jgzsv&?n9OeA(0i?vv(yie4oQXylp z788kGx~>=0^%+JYcQlND6SXl6G7`C?uFptcpPs&9eEcKgalqpN0MzSs9334Yo6VwF zEMjD21b)9CS65dk6beuj1)kq$F*F3^^B-_}DuF`enND|IRwxvxsw!7jR=BaT!TbAr zmP#eg&(Cvpb(QYx$4RG2A@W|7%aafSe^M+KQLEJ;NfM@}rXb5Qc6N49DwWXJ*Y}cD z8zF#ldC~`;50K@qz5f1wEG;b|91bIuO5yD64B2cJ6B84-xw*mh^)z_Kh@mWA8f+wRPoO~7&RX3Z`xF1WnB%-Pvl zZfQgunHJYo|Evv&2tyi;Sq=}sP%dwx z>vOEEfTqFN7`(hdy$+QMRI3eESAVj%_no=9U&H#L*|L^I*G zhsVc9Y}+Opjk3SLPqW#i-ENb~WSE+o8YBk-(Cxzc`6q=|D~YD@mb<$q$m(}YKiQK%~O#s`r>2x~8Vll$u@L%^KZ@M|s pe>UO?80l<|aBKXheKvwk`x{KUCM*rf$2I@}002ovPDHLkV1j8?GyDJm literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-56.png b/web/www/routino/icons/limit-56.png new file mode 100644 index 0000000000000000000000000000000000000000..42537933592648647d2c468a4924ad1568771594 GIT binary patch literal 690 zcmV;j0!{siP)Aqf}<1Cl{V5fooY6eW52I#@$r>Ci%Y;7;d!zv15d z9Yh4%X|t&$oI~Chn&wxMbivPCM_uxL@Z1GD9aKl z6hx9qOEk@pd_MZhcmY+be$lj?tgnlhru>ymQ)GQzMALFot@>YJmgSIidRtPdw%p(U z9q3i}_adoOThi%mu`CB-7}FAuUrRp!Z=hG@^CI#1wHU^=Y#X}x15|fotujCiN!@PQ&XKssZ=Vg ztgLWzb3?6G!{_s{wY5d5RO0mXlvpgr;Naj(SW%$SfP;fK4({%zh(!2R<#L%uqk+@u zBpQu!b#=w%1VPSzpBEjC?9>>SWsH#dV z79*3%P%4$|Ft;0AE*!L4@ceA^>h(IoV37Iwc~n(JQ4|IS2AG?hV`yjy(=_d{=VxfO zaGTY(iHU3kdb)jj0 Y0Nw*3)XVZQz5oCK07*qoM6N<$g3`M^g#Z8m literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-57.png b/web/www/routino/icons/limit-57.png new file mode 100644 index 0000000000000000000000000000000000000000..97f09e6b160135648456c98b960ce2743d7f152a GIT binary patch literal 661 zcmV;G0&4wZ1cM^6*tKLbhoWf-u`K0ntwTiUP%aO%yZe=ki#1er zgk%!pap>!Vrza?tAfJaqq0HjqFSfRRFf#JHQ{OS``n0I3CaIJy#iEF9%ZJ#uNUU79^JwF-`dtrYVxkiKuEp%H`o_nQc2Io86Igx-GTZ=R_~m zY9i@$Te8_5v27=!X)}^Y+=;ILo9G2y7fB@UMAK#*vc7(V&-aDBJ#ab!czk^1?CgyD z`+K}zFB20J7={6WqA1MH&N4ne{`~3fHf(M}qw!Pu=5TyxZ7o1F+G)z|?JXM{8&oP4 z>h(Haub0!)Q?9PAI6gimnN0qbIULYz!pVu-MYS3w9_KYTH#an!O%z2T5{a;~vcl@> zD*OBUgu`JL78bhliN~Q@jk<8Tz~kwP4Gs>nyu8fR)D#B?2ZTZ)!r?HNmzQjBZ{vBB zkH-Tp7fvh-TCJ{FqtRe=bd;r~C5DEEczAf={QMlh-%lhG;T^3OSQbwFeuGk}D>gPZ zMn0b>lgThSIm!I|JceN~H#djd?S3ySmB8;eKI|rdX`0mQbpn9^{r&w^DisC>2JrcO v-J)0B?CG5KcmsO6n?1Z5|LLCf;L`p9H+&bm0u}`z00000NkvXXu0mjfYQ8Vx literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-58.png b/web/www/routino/icons/limit-58.png new file mode 100644 index 0000000000000000000000000000000000000000..b0f127decb9980d95a32df1c8a728ca80989c575 GIT binary patch literal 688 zcmV;h0#E&kP)9P3d4EJ@8HE+~4xu zdk_(NUc2oS!-$KnABw8pNGK!{4vU0BBC2{Lx_&5z5tnw``RA-6B6L-)j8)SoxM*5VG8qxmlvl|#MKT!?P0LBOI{pl^ZHJ`OpCpy)NWK0# z(2MGIkyNT9>GUVDZ3ki)ixQ9DNuls>pcfSiBJud07{;P==jrJKGc#||bx;%ln$0Gs zr>EredCKK7!C(->Fu1t5z_x9I!63@dEMsGEb>-vo(&^k$l#i^g28cwupHit**x1kVWQ2S^PpwwN z49YoUuni=$$@b#u%pC;`doa0#dgSrmj6g^ck zAtJQAdfg?asfl6iN+fbF!Jx?Cphz$%5{aCPVeE=&YErMe{_Ju*Ui0AId*ypD9(a0!Oa_ukNTqU2Pyb+b^(+1TKb!qcvt^BmuA35#+LBI-*tYx?+ZIWu zMWRt#blnuo8hdd(i*mVu=z2=xagkzC-bAq|5|4}MdP;J+z_ZM@owByJDX~~X3Wc|c zUMLhqVzGv-t!;{JI}y_y7frhs%lbFb3zj9KY1d+!9c2E?B+0%ac6OGjsVTO%w<(oM+~41`w6ug} zS=4H^R+-xkih`4R9jeuqtXwWLFfhQ(%nUxCkH^PHhKGk49UY~wua8hD)GDi1p^7ppRS(`VYt-IOAtMQ-iSsO0x YFF>d$C^?Y-X8-^I07*qoM6N<$g1A;i+5i9m literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-6.0.png b/web/www/routino/icons/limit-6.0.png new file mode 100644 index 0000000000000000000000000000000000000000..60971c33f89acae7391ac372baf78f25c96a2ad0 GIT binary patch literal 716 zcmV;-0yF)IP)8(JS4A}X9m(VujF0~?>rJw%E>TegV=;q;0%;ihs|>e4Ht1FBa2RFoW3DYDt*o78NQsT8RwIaaIw4$Lql?(Xg}9&fYJcpKhvk=FDwS|@a)Q3TK6t&}PSbrppj3K?vok9w%Oz$q=0&%+x7^s+;QIPHH#awV zb#+COyuQBX+S(ddS63O0MtO5{(|uhgL&|bVLcP8SSvG$+pU>m+@)Ay`6Eib27#th~ z00<#qx7%@abOfih(nlHXC5Ko4%%L7#$tO^71mW*(}QCvgtb<4vdV9 zU}$IvPft%>nB5N8Y>=SqK&xf?E|&|#!^2ovS%KT_#{K<0DwPV{Za3=nI)o5-etyQp z#6%a?Y5}?q2_8=w#iHp)A`whZPGWm|8?&>sSXfvIeTB>Q5eEl=Bmn>(A0JVx)!_Ad(cj;XySqF1d_HKJhFYzLv9U1>3=DKY y?KZHp13Wx@>*<{JcmsO6n>|d8|CDDvNZOwpL`<9ExHZlI0000nSlid6|STEQQu#i4*mLI&&IfrxJ z7fHNWt(HfYl~6jpt%->f#bVOXkQ9qa6B8#&r?+KU3AI|DUu#{GaHv$GY;JxamwSh4 z4vFFu^`};JT zO`_2#uAezTKA*?7ZGyqzQxJ*3@$nn-c@Hv-W9{xb8(m&rYJPrRb8~ZATwK)o`MD&i z(P(IIZ%^@fTsu2EFHg6-D;dVIn_6ug!*F&!JUry=>U22 zUtgzIs{s%Sg`BH9lu8f|mt9(3{z4$|o~aU;sqoL7zGDG38df)3PEVe-8~(|la>xGq!0X-2cFOG z3*Q$c@yqkP2Dxrhnal@`jvlC|N9ya7dU~YM(F0{NALP18d0xZs*@z@URVs1bzI{bD z`y9*aCY1uyga;4c`*$c7;ma4;-K~&Le`aav9o^mE!hA^1=M%ClS95c|3I)mc^+)-> zR47PubG|IgRX(4%W88u&mAEWxSF5Yi^|k&=*VoeOs$^Nas#M~)FyD`Ad3jy)^MS6e z{tk4ft1D@KKG5>=x_m#1T=%Jx$+GhKe*@hqpO=!!vRwCR!|Ph@4UZo`Ba?wh1c1ZC zL$=Ds#J!;el*PBb+&rOC-j z&CJZ``1n|o2>=>FNDQnI>FbK8e!U2u)3)U#rzaF+OQ}=>V0?U> zp`jrb78V#B93-7i-@}@kps|rCo(DgEguY=Iw6?Y~F)=}Vdpqan=akE3wzs!gSy^FZ zWCYu`!-|31yyp?6qoYKz82YJHir(H{Y};mFV1VJ_Vb<2xC=?0+IF5sD+wAP@+{21R z=;$a#^y0-w4C5v5-$OJCz|GAK7Z(?Fc6Jhr#W*`VBNmHMtyT$w0D!KpE)2tfAb`b1 us8&DKbtGTv6&wU1m?Fj>P{f4f=ItORv~*}8ec>!Goag)C zobw`yU#ijQlH_{F{b`q(_gWp&@B}{F~D0O*u|njYik+y@n)ODwiX?fB%|n z_Boan#kRpT;lTr_R-sUUFJEAHw@fnmnKy4f5RHCs^|#2m+*4VWqxpGP`Ml)1`lDP| z%IBr|c~_R@D3^PB&$t7X%Mn@Du2xp0>udd$uCJw)70I%8RW3*FV6N-Y^76V;siv;3 z{tk4nt1Bs$YHE3TU9Rgvj+0P4UQ#aiZ=ieSa#B2AlH(-0-q!2yc>MSo=`+54;VuEZoOR-o4 zAQp=;IXOwOSmg5Z60g_WhWUKp_2Qw?fLg8P2Lb_t!5~vpQ}p-ubAEnKsZ=79$*{V* z%HrZ8!C;Wn)6+JrR)aQA%TVr^5nC|Xw78Vv5 z92~^9?KZ4XfKaGt=*5eV1OhMF*Z_|QfSa2eDwPTY0|WH*^l)}|MmQX%TCGy4R0xGa z1OkCOP_qfIUcrwapE|l{9ln5$-ew0=<3HtD2a@(DOwUeipANfO00000NkvXXu0mjf Df2m(N literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-6.4.png b/web/www/routino/icons/limit-6.4.png new file mode 100644 index 0000000000000000000000000000000000000000..73937e6a24dc41e53d9df9a48304f67abe8bfa6d GIT binary patch literal 687 zcmV;g0#N;lP)+!t*y_<=RZMF z#xOSr$THB^2RuIm$H&0I0Z=IDSXlUh&CPEZ8#CN|m#k@1R1}rbsKa8BbR51Z$03VF zG8%QLC@M8=s>5i5bUjE#DKMQT8x6iojRu)clZsNHt_RyN#}SxHWf+UKXqxW>b!3`k zEY@Nwm7(JZpsLO=61kzK{TrwwO(P?b8>;Gz_lsqH#l*x%#N&X+0{}QbKga(5J}k>Z zFc^gAm*0t%%VkKC)b;}bpj7&R!$U79%Oz&B?nT$v*IZs+=F-vM<|!e7#J8pC=^0}e?I`=@$nIvOa`@D4LvdKG(M_3qwOgn4X?SKA*?w=_vppm&+lS%OMmB;p*xN7Z(>VFrN<) zMF_BMpxJbNzu%9+!9gr8E@F6i7}aVOl}ZJzRtvH$!!QgOhJi|@@&ao%0o%r3Ju^*{ z$z+nVv$I@ZU+2lm2@{C~Z*Ol&l6!l5OeT|W_RPbzwO#oA32bizg5dV2R;yuTWCXpv zy|}x(Lm&`YF__9sli VgsJ8!zB2#-002ovPDHLkV1mR#H46X$ literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-6.5.png b/web/www/routino/icons/limit-6.5.png new file mode 100644 index 0000000000000000000000000000000000000000..43c2feba9ea6adb6ad7ae16ea53f979ec7034eb9 GIT binary patch literal 726 zcmV;{0xA88P)cQ!k8XP$uIMO^Zn+B2p;Gn^Y)>L?R-Z7L!cI^9(ahi>$A2%Hm>O^7*%c zUX;&^EH2h%eSK3*(}EaAPy&HFNhJOa^rA#UBoMe0!w6bGRjZ#F8F`PcgQ5U%d3njf z!2#836_3Y*q9{B*KC-{R&*|wY>2#X@{(fvW8@SzYe(vDp#A^MdC|_7u=w)&efSa2e zB9REGREl^!j^FRc>2z{^ea*_s3fXLyd_GSw7{uXlJP%MR!QS3G3%T4RK3`+I@pzo8 zt1DDhWps3uj*boh(&;p%QVFZoir4GK<#N5Al+Opb+@yt;7HDg0_|<9^r_+hw?D#auu27LH7s;@r${Cnekc@TXlRJ#dk#+aCxU}IwgRaH4Y zK1SDd#>dAAhr_S1WD>f&Q;IAteZy}5!1gv+EC7_tWgZ?LaJgKxwziVZW^udSl*?rb zg#x|3y|lNtKZELZSY3r;@q1J6tjQnH)Zc7kYW$}>YeLfg0=Kd;YNruE_5c6?07*qo IM6N<$g7Ym-K>z>% literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-6.6.png b/web/www/routino/icons/limit-6.6.png new file mode 100644 index 0000000000000000000000000000000000000000..3bea79043debab5ba1577b6dacc71a06e8cc336a GIT binary patch literal 636 zcmV-?0)zdDP)kWmN(FUu)t0uBK|xFRYZgh2G}d^(61Qi`NtANZCZyuS~=?|om8 z#G7LncFJ;^g~BD1$$Lg3zb;s<+IFvO`Idnai1MG)U9rUt^kNqe13! zCM8LxqU-GzCzWcbQK2s3`v?T0~LEbh<}b-nD<#^>2tqKcY|oY&HPE!@~ovudku&Izpik zY|D5s2((%saC>V9MX|+d)w-zL?J}Fq^6>DGM@L6|e0*HWNs`qnDT*xtqtP}*(fZxH zyE}9`9c*lDAQp>ZeSLi?2QY7+(P&$M!vVNl7T0whUauFaR0_3P4efS&DF-mGcQ_!x zFo4O#;%>Ja9*+kH2M6%^d>9M{==FMVxm<6!rfILT$pkPA2=M#cXf`dL$z-s(xrt)2 zh}VzG!=EQY0>M zx;U0r+)HtCuoPF@t%I|JgGf*aK9?9YQ5+-zAtm@1il8LVuY>rkr9%tp2X{G_?{^Q} zb3P>T%yGP$on6r65os8lLMA`v_u4~0U3)zww@_V&o<^E5Rz5ekLi`E%Ice?~f8hYVw1D=Y3p#bQyT zqoW!b8PV9-n2wK+B}wUYS`!l!8Xg{2XJ@C@*ViArTUn6|W8cU5c`t_H{_f7s4o62v z7=}SK8YK`20AN`bmSwTDv_wx&4}*h)k0WImaDLwF#peUR->p|F6+)p9{r&xQUgETicGc+_rOG^u7JO1sum=V`GDG zI7}=Sp<2aDIGiJ!b?b>lf@n0#%*+hk-QC1uG3MvzDVNI}9v)&@7WMV@Phr_C zgu^+Hrl-FU48Gy>XYhIfxW2yT?Cgxz)>axC8Yq{`w6(QyadAN)5Fi)~K7#J<;O$%Z t@#AYv=d8vXP}AM4VQT!RJgY&{{sio5OFnA5>0tl>002ovPDHLkV1kioN(2A^ literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-6.8.png b/web/www/routino/icons/limit-6.8.png new file mode 100644 index 0000000000000000000000000000000000000000..f20656c0a7f1056156d2edda39ccac30ea33b280 GIT binary patch literal 722 zcmV;@0xkWCP)%v1 zgN~&Y_fm9mu(-F~I`|j32!UYn#vukw1qbnhkSZD?NTeaTz8%DbmJThXFP!Ct^L!ti zb6zCzOVw&EvhA3X$&VTz|E9q~>G5M}a8Mc_|E6T}qij2-TCL^xUPBUf6$)WCHr|j* zy}+^th{wS+;n5@b@dI)>NT*?MufX%?UwHfW0|Nu!>-}|dHXD^?*_xksb#NfLuKp<3 zl@1Q1`FU5CWhH1oKrR!^HWks^AJrxS!dzkBbw7k5og@rqn zN`D7>P^ly>EZk{%d0npSLAL!=vDlfi*?$8)D4UgHu`}8B)0TJD>U*9%c}6k`h5^9Q z(GlC*+f=Jn!r?H6Vc@zhsZ@$|I*seP^!N8;7zTtwaCjKt>(>@!nuprjsz2m7juMH4 zW@ctIJ3FhBlM_kO&d!b^k%;Ez<}@`mrA#K%_}$i)WSWN_ip4QZvp(HSCd2XZFJ`%cmODu%UoSu5ex=tZ*S+~;(|~pM7dn1R4Ne+ z2J!iP_n^Bwc>Nk~Zaz14&zgJzO})(~rpABDvnC|%Pu0{^Ayn22zyJUM07*qoM6N<$ Ef)?Igp8x;= literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-6.9.png b/web/www/routino/icons/limit-6.9.png new file mode 100644 index 0000000000000000000000000000000000000000..c401e26f9f26a1bbd352fa44ceb6a1afed08f072 GIT binary patch literal 713 zcmV;)0yh1LP)>tKD-(xHX)gS&iie&=%U zIUkbv>%QgLvlnDC5C{NpaB#rd z+8VCwl1L;71Oim6RUF4*eSMw!`g&rq*!`o&b9($Ue8Bxz@7N9lB0larGg9Ub+3Nz&@7WEguvE-s#982<0(^LY*r4{2_0CYemq z)YRnPHX4nxyu3^(6e6F`KaP}Pz{SP$AfXU6Hu}Eny0o;kFgQ3!E|;TNECP^9rMSAf zBAdsSu>Ctw^EZ`{{I=o}M1E*(|-iz4Z6@v$V7XK($(BV`GD{u`x{3e1sJW z(AHKAXm<7!k;p4PeuQ8Ufa~jP&d<+@#bPuxG;n%)N<1Ft=H`ZSxlDU|JK=DcpB-Rs v4!(c?T+=zL@dng%H*1(0|0&OEkhDJl9wt&#(+&{M00000NkvXXu0mjfEuv9K literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-6.png b/web/www/routino/icons/limit-6.png new file mode 100644 index 0000000000000000000000000000000000000000..a7e700888f1de069861f998f71b871147f80098e GIT binary patch literal 580 zcmV-K0=xZ*P)uZXNs!9ECveTuwnFI0!=Ex+oq5A#!&<9fCsb4Jp_keB1N>zK@sheL)g` z9NYF%(`2gZJu{gnrc&h55t&MnnamSa^`4q0)3&|4wH}h#Vi*bB-hM{A{Sk_iL@ozN z5^!(;yuJecKJf4WbUFquE`H+r`WuqTU)%bvS=UdgC>kpjhrJ%@ICRBv$X<`ER2(Xb zMqNMMINn5tk)Wb<*l3WZ$#-FzWTQbUN{5D#c#}Dfz4^Fe@Z1FMy5CKL)G z91f#UC?Fb*!Zb~H*=hyYHUz|CL-hNunOrW1bUKY%tp-sPk(!0000|L^^I z@BKfJglpNhm#Ug%K7T|>x@00j&d-yH1Sv_E%;%4&s!7_m_s?1nNepBdaU2|c!r9pe zD9Q}dX+V~Nu`%H331~Ec^K+nDHIT{tK%wvzGc!L2^#ih|EmKicX0s0Kb<%P8S2+$@ zuanuVLq$=kY0EE+XOLmUsVG&JN@SuMhOC?fKsx*xFGt6-W7K{7L<@#*5-v)Y7 zyG`bDeHM%RbQ}Rx)fAJ-J8Ig$fnKC(WHNb2RZV$6ThVjERW}tgfyi5{V!X2*5N=sH%!?w~N`?SvRcL18f@tqEQ`M+**8y QbpQYW07*qoM6N<$f|3_K8UO$Q literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-61.png b/web/www/routino/icons/limit-61.png new file mode 100644 index 0000000000000000000000000000000000000000..610ca739ae539060948d8ce5b4ae0fbf82ff0c47 GIT binary patch literal 642 zcmV-|0)737P)gIWiU_6NyU}iwD&86dlL&XRSmMLrgP*y}eI3J^cVp ziz1x{R27(*03IHIMgurI18OxBYimESv-1_v=+9yO&}ykNaf3c*(qf~?oc zd|pt~bQ;F;ljBijnh|PRjpZ_FS$q?gMV8B?rqyVgkw=*jGE1dh778x={kMsp==aG& z!DXqmOCe-X*H@TIUDGiBP4t9ekg3!)b$!M2*|xu6e*Qg*ML?2WbNHmYGb38yg#Jx7$2AI$}H? z=i%Yu%hOdVq^dS$^m+1-GLa;0g ze`PQL90xMO;R`exBUw6~#_a4Ywzjsgu&{vD)l~pMC=|lvREWA1>l_U0I;#($b; cW0MfUfhdS4gfn0i9D|U87!0VO++E%dA)!|~SV$lErssKo!^itR zhzP#tx&g7Qlw`BJl1N-hG%7MaE)tE3BobGW&F+e2rNnguf6gi*LR-~pgfCw{adfnV zu8)vTLox~7-SF}Pr4pQ+z}Z=qOy&pc>t7ie`PrUtlWluOblsB0MNcj+L_AOaD$f(S zxDZ)f^hDP!vF(`_qY0{3BckhPlFy6O>+&Ym>mvEQh_0VWwHj%{JWrKeZcCPz8}jh* zHc(3s4B9yrlNdiX(`OqR2x*ORLJM^l*?s` z#Uk~3okF3&-Q68~dwV1j2}Vapn_)$PrzhCof2UHbO_5CUyUx$gxw*Naudk1IJWfwf z4+{$m?Ck8IX&RME#g`|Ugj#J%rK<~qL7(S14g&)N%+AhYnkGX-LrhLivbws;#KZ)d zOvVoj2BE7971xE=SD&Y88vXtK%+Jpg3WYd0IACLAgX!sM48x$ex7QDQeFfJ=WpJ=e zspRw0=`>?wV;F`(JRT<&i{Us9rfFgr2FJ(8epsmlgM(#7R#v{DX&>3%232kQ&(F`? z-`^7og>W2)TCGOw^upmVnx-{_jRvf(!QNMPkq*bru_wuPa__1 SXna@z0000E(%`-!u&1iJ1c z8ijBeMn>S}1quZ?KZjhdOf2@D&CM_Pd_Vg0eX?oJiLM(Gk6V(@i&&QYRhA`^&x^$4 zmgu@6ra9MRyn@Q*NzwJ3WHKW4y1YsCx=1D?qU$*+mnUCgmSvN4dPkDUuGDI81NBs^ zi6oO|gUe@`Zpp;#<(b#;ZR zs${cSN~ID92M5f~&f@p`U&D$5%_bZkzOzxS1__7xU6+@a+}+*bbUK-tnL*Pu5{U${ zSPZAr$@BB`K%Q_Is@0$kRRxD*z-zbLjE#*kKR?gu=_!pygT=)~ip3(2kB_LTItX(( zKvl8P=|HPB;JI8b+-^5ZOG|h>9*&NV*xK4+b#)cD+fAud8ici4(CJ`9(~1-d170*5 zWol}QwY4<@fdCsD8vv}VtS~V#K{Og2gcS;)X+=erm%rk2ePnMBY_`7t^z=lfQo-Z# zFgiL)v)QClsi0{ZE|=>UsN040b!arc4Rz0kd;vqf%^|kNf9kU#H0@99aUVJqRXEK6 O0000LoK~y-)wUj+bBS93$|C>Z2Vop-1MA$+>3z0B_QMU0EECma#ask6t zX++MD{jq!PDkRayFk;x={({59Ptddx z%+CWt0KL7y(-Uxd3LG5)#iD`5#b4Ok`i_wivmM_y>-sb`O){DESSpd8$2ajjvQ#3I zNspQ)sq51%$Fs;VV$`%Eb2+kJ=ewxa$y|=qv?2{7_AK+f0JGUVQz@Ue{XS6(+a^;f zpV@4lo)-Y6oMj?$LtXzjQ43us6Nwv2IUD@yINvZf_7UkcpeXHHRw@-792~%L9K>QV zD2jqgrGjd;3RP91w9XC>1Lg7u93KZkA<8TiIxf4qy5h>p3YV9cxwf{(i;D|dmc@mI z1+K2H(tG_lg#sx=Ie=Pi3PRvdPEJlxtyajMBhJUpP$Xkch)=p`%P y2R1i>`}?0={byb7fUfpt7pKO5nrB^@w7&s&xP4d`r_oUW0000qJA$<_8L<~~68^@p){R<{_nN7=> z#AOfzvuV-Lx@LB>YhAmfAiTCvAq#3%BuyJ}+Vz@>dy#NIowT zj~k*Wn&fgbuZ%XRTpkfc$w(?CQme_ERI7=kQX-0yk#c#Y4Kob0B$Hc`NVKF{eH-Xi z)v8D$(UN3xOANz|Xj(+V;XBFY{tfi1Tuvk$z7tK0ST>u@PfSg{M^(XO>ip&9SF)9K{k;DBs4%k}j&V`F33?e=zXco@#lot&OpESo0NXO@<{1Ogqh8RElD; z$l2K$Znv9_jScSa@2OU+L?RKKPG>u8GQram?C-xbQ>g?9h4@|BY?iC5D+UGz@caFg zN+s&`Iu?tCU@(Zs_`hBX?X>zHx7 zixdi7UNjnIVq${T)m5gar&(TJCY?^Bsw#fJpI9u`4J#DD?Jk;RW#ual#|L(Hz-;dL z&(F_1JUrm>c!q)+?-!`mg0(fM*T402&w6|TJ-y8yw#I+zvmP|< YPp_3B%dYl!K>z>%07*qoM6N<$g7Qg00{{R3 literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-66.png b/web/www/routino/icons/limit-66.png new file mode 100644 index 0000000000000000000000000000000000000000..30c6f05a32c325870eb4a285cc9d1621da00c193 GIT binary patch literal 653 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 SL@X`<00002jKv&QtEA)buW}r+S|wvK zhqA0tRhQlwZy;R{QkE;s=gD@PA5yzb=JTX1SE%d3H<;tNn9J=lk$7dR^)b-9S}ih> zcx5iPOUH46iV|Tsd`DINH_*FOl?;dPs3;NlXUqD6xw%!OQozUvn$0FoPEK%fae;ch zjzA!Qv9U1#fTn3E7K`wDy_lGocx(FPB%o=ZP%gW{P)K8;(EF%dE;AmFbA5fC)6>%| z7K+5T7Y;16CZH>`rl+9+dUtOU&r`^;oA>~uOL$)lqqZf$MR zFbr;PZu0o}xc|7(AVrZbcszj5hhKGbbAwW;gzfEZ_77E0gH=^czJoj!@~o{$H!q91{6iX^Yb%iW@h?^ZWpj^xF86Z zsMQ8Se06n&<>h5~yN1B1u}7#rykvT9!rAG2W=3A%3R*W3U}c$nraj*dR!@-hWQ znZxELAj`nm81V7})a$_26;Q33*xvq%{rxYPoBQVFduGE}qoU|cCIxFXQV9MLA;?;d zOeO^tMWuL7Jv9l}d4EXNR#^j29OdBuU%0 zSt^ydxVXr(vortgibYbE8(~=1DrDI^Syff2s*2;|V=ON(V`F2(`}IH|fXT^8{Dv$8 zmbDs0I1EH0zF526Mx|20-rgP}KVqY!quAZu#nRFe27`fL7Ks4iFoJMhpx^h!%H=X7 zNy5s?3T)fP-Q68*+s0q%_W{>M5R!C*dfivVx3{-YRTU#6Bd{zB2L}hREDHcIJw1)_ z@o~SbUI!%U2FzqmnalYq{^8+)Pft%INyl;6>2&Bg4oULy@sZEZ&;E|c<;YCtbf|SU n_YP)dBAkOYi@58Q3mUq( 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 Ef^NJmmjD0& literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-71.png b/web/www/routino/icons/limit-71.png new file mode 100644 index 0000000000000000000000000000000000000000..2649962305314bb9ee65a329614ea8c322706663 GIT binary patch literal 628 zcmV-)0*n2LP)i@R=clJHSXudid>(MQ001{PH#j>x!`0Ol z+U+*{em_E?5OiIK5CWl4==IVT7JybO2wiuBiqc}GGPanm>&#}e+}_^i($W&E)hbU< zPZ^8FSSpn!cUP&9iqewM>#adi@H?8Op3Bt3j4!tgf!&uMCHP zZ9{@AU!&O^%SfWr=|Iyo%*@P;=ELDId_Lc#tl0!)`P#+3y>AEvKH~Tokfc%G?RMew z`Nq!a{{9|wb90!VpMMoO4zRxuJUo1#>N}hA518uToMPAbPyK8Po%R<=NijqOT6xU? O0000udI6sgul4ZofKsW1-Q8V8A`!T*i*!1Ta5#)uEH-KO`2epM zB5WJz_y2+w3I)ir3{_Q8EEbW?W)X=*kV>Tx2m~h0{XSsZ5FyL=s8%OtBvG%|vAVhn zQ54Z?wcxrg3WWkvsT4}3(j=@}1!VbN;MUf6NYW=98~~y?p10d=1Ofp_l7wEbhsVc9 zjG`9|1|dn((CE6r_BQbJ^kb_3Y|06kYBs0X8vm)!rqHy%0ZwjQO__OGQvd(}07*qo IM6N<$f<~G#t^fc4 literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-73.png b/web/www/routino/icons/limit-73.png new file mode 100644 index 0000000000000000000000000000000000000000..24ba4e4fceaf174faf22948cb1ff843ddcd327e9 GIT binary patch literal 678 zcmV;X0$KfuP)+T(8a+bf|Ko52$IcB1yPVZ9Fm|38CBF2!I*#n4ZfGt!5SLd!9x1MUGDjvZ@Kpz zM1-zm+nQKbR8px835UxP42ld6iUflq;c!_}sSUBLsMxmlvR4riI#jCztgL)wZ!dvi z1c=2T5&@3~9v+}rgo6Xf<*H0herJCE3xU9oPJhRo&yR{>SQ3vrQYeTxj{FtJ5h)Zz z;&Dd|!;*Y{^x5$ws#XWYFmjSki!>VYDjE%ubXvqPa#F1hJjonKm4$_6NhaG;ufI<8 zOua6WOtxiVVObnUMJ#JvqR|V<=l@OgOg=9Xjb4aljcZGfkDnMCdQU0^iUPpt=_y-V zTO1!BQ!bbBcs!h+pR>KaO{>)+5D1|B%JS|VoSgVLJk+!$Mfpr3@s?1i^C*={g-j;H z(a{l`o16H2KGxUQxxBn2lgThLGD0vId9n&;1O_Q~?HA0~f zQ&Us;{eEoQCY#N2e}CVVClY~LEu`Xdf!obX&d$!*-`^*ZNH9D+%*4b5rBaF8+gn^N zS9jR$2A2yJ+lE%_583YSF1oH04u{#<*Uk=fZ<{C+=IS6AKPRtsz!6TUIQBu9p;+uBibT)gqHU85+>%pe|3G#{}tCh#k+yDRo M07*qoM6N<$g8o)EhyVZp literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-74.png b/web/www/routino/icons/limit-74.png new file mode 100644 index 0000000000000000000000000000000000000000..f500d8b3f01400e252a899a4cd758bf4e2aea700 GIT binary patch literal 652 zcmV;70(1R|P)(b^WQ_Ot!@h~xIg#~-{Zaid%W*^ z4$gDm^8 zvI0mFFfsx>JppHD!09PaD(P5VJwzh$4Sv7Ti|;9G+7e}1Wi)EBTqZ4xZ(>YKgFJhJ@Fqzz8JZ`hqdY@32R*Q_s zZ6=dDv@8Ks)n$f4H`KI$6Y8RAWGHk)Rb6&|d4B$i+1Za!6u{vC0Mu$VWV2bEpP!>z zt-|B+U~Fs*7Z(?}y1IfWig5h$#pEPVseC}O=maIH!c3+wS+Q7TEEeP1+8U>)r^ zd3SfmNF>7b^>tcqA0$eV@?ZzP2*ViZ%3Q!aUxm*s%$H$nPn?tQu!{y~=zq8v7xLgomnn0)X zhgdF`!}$0(=I7@j2m*q^APmERVHl{_>;2B14q%#ivuCz#Gm%Izolf`eBzbgn#Ldmk zzxK?*jg9Z{dOu-r4-kZ2vPPqU(a}+Oz22Vf;o$*~kB^v`n0T$qwt=lJ;Qs!{Q2*JG mCt#?vImE5;pZ3`hHtkQis(3R8T!2jg0000T1yfV-{0z+|)M`+#JFKn!Vte~L3k!Fn`jOeTSHv(Z$>)7(G(>z~-o^Jt8V!+r z-WS8L#I{#n97B=g%!^^vrCb(qUHK5MD^f0t7)D(jXMQO2{eYB8J5neNq|^DB=!H&4 zq)-@0sk9@$A3!WCBkA<5*!I7PUa)PEboy2-E2GHf<_{u~&+P6(AOOJi^)+W_XDEt- zs;cO^&ehcwuImyGhsot~%+AgZFMTk8tu5&FeyU#;P%aP4y+27x%TCI+ULm>zT3E+9q z@Bbw`IXNL3jk3JFOt;%5kw~z62eoDc5m{?50!=bnRz z&~;iZyBJ1T5{Z2Y28-hJiHwhn_A#gh3@e%TQI6H$(ro_U+57yScGB#H3)OXC;?2KrdA+eYxxtxe)$zQQ7kz7tB z7PCat49R9^UL4P&QpqctmXTCSq+XXdQLl@nQX-m`ky6R~EVC>{lF4m}$JRb8v9L z<>e*CVv&J?0VK*6jc9n~Z2_xbUIC;Q0NXj z9H6Qwv|7+;{2@CzIl<%cFgZC%e}6xNgM%zCE;2kkOub(34mTRmYN6oq6v*eh?nDIB zG|_b(o6SZv8pZGTv$C?n#KZ*i^Yh)Zd>%ZWf=!l}zu|U&WOui#36)9(m&=9Q?FQiC z;eormJ4QxEy5FqbhSgQLzyIFTf7as(=;>_saBKXheb$3b`xC%J8&yFnBB=lX002ov JPDHLkV1htPJ$L{B literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-77.png b/web/www/routino/icons/limit-77.png new file mode 100644 index 0000000000000000000000000000000000000000..c89d52220fd07a568133f9ebe38b326261163caf GIT binary patch literal 574 zcmV-E0>S->P)jK~y-)wUohX;!qTZe`6#?a1{^~vZz}@+C{_&E%Y@Uv0+}ISaH!O zD0I=GuIudPB}6FJg;_-lt&)W(6cQ9fp&Nz7e->h9tXXux`QR>oF6V=L&%FmEv9nw^ zOv6yA>yON2-l!<#@iD0=WG3@QU4NuusC3=%=h_fS_}F#=_xEKyJ$-|wC2@WZoSXnh zN5FCk^m@SaGtlYSxVZR*>+2s#CLO=tH=E`uHO*k5;IZ2!J&%ES9@*`Zg@Q*-GiaKp zTgOIZ+X-q~ht(>%Snx|M7G$+bYFdZ3o!H1cPhh2T$6|5K`TT35E%P~9EUsCp+|ly{ z&@i%8)pwfazlpY(CaJ3LG>mMBmzQfvYRvpF(RA*RzQ`u#rY z_4*%~AOL4)!0l}oR20&(0#8t@)mScGair8YPC=*l^{vd zUfw~H08xYh*9BIqJz2Bage=QQr_+IXht&#jT?mlnSM+*2dn7R!43N*~5s5_h@*ce& zAj_|T-2{NiWP)fkidZZbn0@~0=0V@tfq%e3|K literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-78.png b/web/www/routino/icons/limit-78.png new file mode 100644 index 0000000000000000000000000000000000000000..d4cc2e577bf07260b0ac3216df1ab9d7c8ba499a GIT binary patch literal 675 zcmV;U0$lxxP)$6> zzGvGmF$_&Ina>i9)+8JjnVJ#_hee{%nq)Gc#V|CnZP&|QMMUV)XpFPH{hrg)C3Jm) zWD;UA7#f1dN2pZb>${)*#>l*=Ni zlq0%sNU0Qmc07q1jd9WSg5+`{mL;#kvP5z@5nV4xqcQ#@a~xH&*)2(@yV7dCPV`Kx zC6Z2eC7az6$59c(m=jIAmQv~8M9-8;BARwBhB4>b=yX0XJ^hwU1{4JV(=<6gKBii& zQmfVQdc9PuRr2{fj^i*kHiq&u%jhUvTm(5kceyqcTCElb2M0u>Q6?uRpTdd)_xEsguw*-Ship3($W|QIJ;r_7K z3vM?mwhis}AF`8^6GEX7kw^r?Fqoa4Wpi^Azu%8(n*HH+8*CeuQ0S6MrLQI;)a!K; zi3A>xhn1BTOw(j_b(KgYLNb}`msKhd3SBC)y!?eg;2k?VpsKz4&CLxypN~Kw0Dxs# zSeC`e$OwMF|CgxSg|#)fyZbuOIUDc>40JaKcs2ghJsZHK{Q(K5CPJoPq`?3H002ov JPDHLkV1lN}GLirQ literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-79.png b/web/www/routino/icons/limit-79.png new file mode 100644 index 0000000000000000000000000000000000000000..4ef74794d9f0cfc00a042ebedea2840505aeba45 GIT binary patch literal 678 zcmV;X0$KfuP)LKztt5E&X085j@=g)$P4Z-{Lh;=1bVUPVNxlh60Dy84m*{V1l{ z&(suz!=P#K_z3AV938>QNuKHHA1p0>p}+rUy}xdDoCz^aTOtup&d)_WPyUMMiJYH{ zL?WJ;rY(*$@$7gK<@0@FnkSM>ij+$7CQ2odWKzU5Pb8o3dy;vcPZEh0iN$JCEWSn!~BkAS!`}@GBY#7+}s?U zot?C`wXwdwj^FR+^z^hLPdE&P!nhB=9|8ehbA5fy;o%|CXcWt`xWB(=VPOHsai~_S zjp0B5{C<46E>tRi$Wo~kx~?-eHin`ojEsyhIyy>kZ!d#`gN@-z1zZ;&x_(7E-B1$| zve_(_WudAnx~}88E(Zq(%+AhY7)GNkod#XMQe=Mq8^PcQzJ3LtuU@~qyQ8(Wm0&Ok zK)GC|SS-@r-HoPcFQQrv78jvh{@&C%Yw`v(bvK)MHU85*Yr>`d1wi5*-XwQ~%m4rY M07*qoM6N<$f}JNnt^fc4 literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-8.0.png b/web/www/routino/icons/limit-8.0.png new file mode 100644 index 0000000000000000000000000000000000000000..06a4f8421fab8fb94a003a0ee547af984501d4a9 GIT binary patch literal 708 zcmV;#0z3VQP) zl}6laadNP9Z@X3SHz))G;!TDaNISTgA&@F~@q(ZY$@T3ZUfa^aLi)nD9C)Aeo$ov6 zAc@~jtJNXPN-C52sCfLF#>S)s9qm=fVr+;o142@S-IEs_47cF zy1tfHR_?XAxhvQ8Bg;xDnXIW)`Zv&{N+l_otjV%c9dDb>cT7yYB$EN357%|c<#Ocn zd0f|Jcz76}&j&!IQsMCMkU$_nBocXe^k@_+l|GJ-JCI>iba3D$IyySi^z^irmXX0OAW1quKiA^oq81hwluoBrtyZ6IcW@vXM#Yb9&tMqd?=~6@0)YUDM1t<_ZfD#@woO-87XZy>lTau` ze}6yWaM()%Jor01@zZKSqv6Gi#Uit_v+V5b5DteqJ3FIRt1&PzfNk4Ymc`xO9pmHU z{8A3ITKE|p{7Sj(#n;x>s8*}2udfq}#fZgXY;SKfIXTJH)D+olmUui)DwTSImCGP?)lc;HzUIRR@cRL990$j7h(sa;gF!AYFNsE@G@DJXuC5pw8uH5L{vI|q;O6FY qTl=id6VTS#Y~$AWPy4J5oAxK}=TwkY8zvh70000Rzf<%d%BEnjD_&116E?_vp zR?d+AI8s@7*n8K=6~TW&2n0gb6q`V_uu389DY9fi5EFOyy&|}f`vD&09+>8Z@0np{ zz7HfZv|Kkp!^kk7KV>R)%|wD+SRfM#GL^b!K7UHX$k25Izt&15;bU2II6nS}O65It zeIA>efTjTx6TtH`&}sqIDp0Rm*xLGz!^1C_pa0?4`)1QjQ`ZerAYr)V_2q93ZR8Xx}A%wu} z>?|Zn^6$7-t3e2XXf!$q;&Gtej-XZxfST53x$JLradE+wl@;#n>~LdagO`_=B*}ii z&-3$hE-fwb?Cfm#bmcOsX>A#{y$Vh9ckcCi7#kZyI-N!+6vETflmF_HBq1CQ<2N)7 zu-l-3 z(ZJf;8cL-SCMPFxb8~}Er-OdKkH6CE0j>)fvDg(_EkEDe+e4?*!T$a}lF1~J$s|gp z5^UQB08CF$BNPe^%UUfU7Q2$TyZaU4@CTfn0J01Ka2yAY<6vfH2CAy!{{9~EcpQqN z;Njr`!C(+mQ&R(x=K%)?z~ke$k^Zw0cfd${bA(glKh3idOxmBM%1JnetWcf+0000< KMNUMnLSTZ7d^ryQ literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-8.2.png b/web/www/routino/icons/limit-8.2.png new file mode 100644 index 0000000000000000000000000000000000000000..f0cd413c9926909f75a0780f521d2b14bff6f514 GIT binary patch literal 707 zcmV;!0zCbRP)Ci%Y!@FGYTrV&0 z^E^o6m*ct}vhB38*|(aS`mE7W>A?eObX1y}`mAjBt!z6j*X{VdHYAB4wOWE#ubz?5 zKgKjin41H`fbMSi`V}e_C=}rMxJD-Pfz8d=jEsB<>H~ABl#*%MT3+^4E=!)LKgRQ< za#>nl_GFs2N~P4D<5pCwC1je%+S!pDM}HN^k#=?@(>zwKmbjI9URYaOFSWAb>*nU~ zM0dHlkyciGZEd}j=Y^4N&nTU)s#N+n(OpU32+q#>`1r8{8OE6o4uXp;%hLGxxE2=| zH9I@2larHxr&1{`EG)=0&45di4h|&4I15v+Phc3q@3vYkIy*Z_rBcLVF`CUL0KV_D zw6w(j{yy<|oXgA0`<5~csMjYVL?RH42K?&kioU)+ip3)JdYzu09suU&=V>;Ztgo*# zHa12klew2gqY#M@#&x093V5+tWO{m literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-8.3.png b/web/www/routino/icons/limit-8.3.png new file mode 100644 index 0000000000000000000000000000000000000000..01a6b366a6d4c741f0b30d2a2d6dd508b7e67a24 GIT binary patch literal 719 zcmV;=0xk|^2^@#i{D>?z&`>|t z(u$){#HmAzd)uvpzraBu5U3slCIQpI$sw337(GzXgy!V#AjY_g>S}Uo<{0J$xvQk4w|jUzE$em*XVVZuk9OYe=H2dVP$yZ(own zKgG7Aq*7p6@ZbS7n^37hp#b~)b<*iiyn6MHX!L8hzDq8Z;<9Z=^Yfm{Wy$mOM|qx9 zE=%+Co^0DusT99s+=A-$G1>OMR#zq0)nCbVrPWo*w)a)9kKMvN&!?4@H_B!^y1M#1 z(4DTXq-?gMm6bR0JRfqLq!NjmN~M1T-KkWP5{a4|C)xM<`}cJoJ$gbe2Zn*?dF1nX z3WWlm=MjlSFbsoEr^DglAv-%ev|25q(I|#tKsXFXN5g#n+=nddNLyRoi*|Q+H8nM* zOeUk**;ySN97vMN<+4(#lrou&lF6j@_V(^yx3wi%){&1!<1v=i{oPipMIaC$9*;9H zFu?WoH2|h*lFepGr_&4#4RLdGb3am+1&zjIAAUcWX4gMEJ7aiwm}0R=qtT$hzaN0f z$w}ts=BQSyTwY${_xtZ*rU`yOKH6<)wYq+>SY&2qhK-F41_uW@K0c;atFgVk&Dz=; zOG`_PjEr!4ddg4jK)a2PP^e0!()AY>7C1RMVR?C(SS&^?7Gr&Vor#GF0)YUFi;F}e z5mKqtJ*-lJP^fC?*|QG>gU{LA1fLH8*L87Sm(kHtOw;82{G4z&OtabK;^Kl(C`2$A zyajbS@Ztr0`}VP?bJpVx=;?0uFg5;Dp7kJUe*hqXR$<;;bAkW>002ovPDHLkV1oNm BSYH4D literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-8.4.png b/web/www/routino/icons/limit-8.4.png new file mode 100644 index 0000000000000000000000000000000000000000..1ec999b2d1650bc4ad76a29ebee52161682e73e1 GIT binary patch literal 688 zcmV;h0#E&kP)UxZh9W=O80bZ<7MV=C z%w%#D!UyX59AmLdn&!WOUSyhNEOtpxqxrBVq(2#k)7 zLXsq}$7g3}sMTr+hr?ZaCh6H*AJA>ld4+vq1l{)s(O(-oelzl0HV<-1_lPuZnpsdkB^VY<#M>YyF(xl zc#|zu6=*hRe2`^8Q9S$g^)-UQAPmDmv)M#{e?I`AP$-~KC}3)83Ktg_I6puC4O0|A zmf?fr0G*C!Hw*)_v$NRR+QQ)AAZoQ5>h(HY*M+L8uq+FfWuacL<5f9;2#V`S63v-{r!Eeudlz`Gl{FKKQJ_u#?B7l^8o;C+lFo17#<#m zqA0kzxj`rtf-K8;cz8g&-NwksNH;6j1vWN-`}?0g{bxO%fS%4~54XmD+Gjo3w7&qp WXo&)uVugYL0000fbEbhf`MEe4jzbxhUWNmC?4B#hZfQYzU6`U z_kAzl`#wnGhtqDyqBy>l$B*UT3iftbR-3V z{#QXD9UV!Fi-9c5QK^)@VSEGC>m#zP1Ff%1zOP@B?@R0Jl4TvJULW}e3xcTD)?R99 z=~@>TzXrO|#f7xAbgi|umkNR?a-0XsguX$wc73LwznnIJdWad_c6`zcU!F% z-QC?}vsrq3ducYC09310E-x>M#bOwSK`NEH9VycU&$}O`s|(`s&_6pnqrbl&*LCqc zkDi_$0Fuchb8~a#^Le(mw%FU-yM@K$(A7nhb{kr)(05&z>FH@UH#h0)>!VVsaC&-* z=Xp#_OfWk;%i!Q3jYfm-+JSbPC<6l@DVIZkd3l+WlM_}}R>)*BWHK2xHa18mlkD&B zW7{^9lau80`CC}I3an8@rNvG3XU0vb( zK0`x8Vf$QP!_%kGXuR*}oOO5uI=Y)3OpQO3XB|k|ud0Mt=73x~9RL6T07*qoM6N<$ Eg5!Hx)c^nh literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-8.6.png b/web/www/routino/icons/limit-8.6.png new file mode 100644 index 0000000000000000000000000000000000000000..33975646bca945e689d1f1d9a5661cd8c30f15cd GIT binary patch literal 713 zcmV;)0yh1LP)s+ZS5mNLqF>Eb#gWvlV#bOpLcb5D7miwD%X_` z52g8eSC(Zfn~gm(9zdm1P?mL|jSb0h^dvcsw6P&s)`3c;-~-Hcy;@)2*22QQuCAU2 zdeqgGw6Jim_4RGJt{2(%bH(FDWwZYVdQ>(m#p6ZU_Vbo^)#`gfp_e2Q;PK$PF3Dt) zR4Rq*x(p5u;_-L@I6gjRZ*PxkwMsA;eDDJSI6CU#+qV{Enn&8*t#7oyzprRCs=2v2 zO-xKEolZ-V&d$y>Gc%*<>1oZ*&g$gkq;b04UCA_$yp+o$m}dQVE0qduZEeJ2F?>EB zH#au`q|<2%g#uk&U4+A7d_G^JrA!mb=cX^GR*QH;G20ghC-!R#pgy!%R+2 zHek6N3=HHwT3q~0clR4UeFCo+0LO7~90$MOk6{>GTwD+c1h~7qVg>{;I4@@laa@-zT*w}H&3&BTyYTKk zAc;4}wmsCe1T&cf#$ru|!{qcd84i=NSd*E|0W~c_+xGnFb&-T4)11KW?k5zBDJaS$ zmX-in1_lOz$48)E2TCQNQZcc-`~%zDUokoP)A2iGU5`^yG$xaRwHhe||0*HKT8&I5 z1r(&mhyBprTZm%aNAFcgeEIT#i(f3Qcq38773A+3XI}=^i_s_kmv2>5%Dk zkJ;=Fg>ZwKw!lQV^C2FOqtof&;o$)Q zpsFfVRmJ}PK4xcUvADSSI#RL>wA*uTc)frmIrZDyTLgnaRI63A+ieUE4mx+&*Vi~b zJ;mDE8l>MmkR-tCg&VdFbh}QyTCHM!ejfRJ9)76bd04jpB{E zUBI^CMksWFdflmSY;3?V3~X+0A`*!p5{V$6&!g39;qvkls;a{0^Sy=D>p&=U;o|!G zHv|G7v9|}f-2ec~vS3*jMn*;;NfKJE7Dh)$ad&rzp`jrJ0)bahuLo>x0r&Uc`#NWR m-hjUDW*<}IKjm2;lJ*xCJym~n8B9_D0000 zhk*uve?-0h9*Po0 zIt|D&u(}F7KLec(&}ab7rjEnI?>IU6f@t)IQ}38HElEXDnac^b+oTZuYlI-%Z8Db= zR1}q(mYh3gB3+MAQJSn&NXz1zVp(LRLMlp=x*nOygpgP+pD~}G(lp;Dn#VNBe16Ju z`HVtHpsMaOm3pA2{hMeWO(RpO2de76`_s$IXKZb~L!kh;To6K_UazCkXg~;ojg1Ys z7I1{aK(`yf&5awB#9NwQWWWw|TCFm@r!&hJhp z6RfPPAel^JZEX$X@pvHzFl!&f*p=Y%0A8=dA0HnP2n5h-wO|+qd_Lbo4q#UA@j!xY z1CxovTdfxM_V#dkdJ4bak6y2b!C-*v>+4s%-|yqM=K!`13BlkUIvs}>i$(PNeUwTi z#N%ZzG*f|B`h&Krndc;?dDptgnB-#RVWq007IfU|AMI zp%A=YFNVV*!r?GRqY+HgL?{%3-|wG^rc>bf7kH`lF1I z_V=Z^IU~n$<$0-F#to=c>XYN_YGp<8ef^buUs_p_9A{UhQr`{Cn4p%IUuk~6p-ScN zK)0$?r1|-VmX}}2m>_c93EB3UJn!E?xAHv6w$J3c6KyYRwN>ule@G?+fdIxBve_)T zTn=Lli9~`xAV8zh!1Fw|x3>w0!^C2-n@5kw;ozW)k00BRWgTd9vsq|sYfGb}qne$a z)#T))^7*_ZX>V^&>2z8%Gc%f+n$q#{@!jn1u356gMX~xgb&*|#wqEIMME|+O|Q0^{T3tgWrl+1bg_(GjPorx;@x9v)_7 zWQ0T_!QkK^KeYq(Izf7RK2a<-;|mK5oSdAnxVT6%nIxG^vbwqoK%>!MXJ?1$>1k}+ zzJnEu(9`oNpeIk>aqr$!-oAxk5CGrz@qM3IEJh>};o{RLs@&kjof0000T5f_oD;E-%$&~v!JIau;XxL6@l!8J7E zUW!wOlUZNyH8bR515$00i%QqxAXtn`aa2#M|XCF}Jym&^BwHY}H9y}o9~m zAr^~awOaY+peTTCA4>>@fJnqQvsf$;kH=vc25j3#G#d4nMIt~b1PPu8xUO%;Fboun zMRdDe$g+(2eC{uEUBL4oA(6Plbn2UFHk+8w=V-NB$mMdVR4V?m=@dvL?gBhJ`;Ptn zPq@AYBEvD1IH;|tovSe6 T0CBgS00000NkvXXu0mjf$Ug#( literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-80.png b/web/www/routino/icons/limit-80.png new file mode 100644 index 0000000000000000000000000000000000000000..c4459286b119c588230cd7b4f6b701c17d68dcf7 GIT binary patch literal 678 zcmV;X0$KfuP)K*Or->CHBt!vRYH)p8ktH7 zYMM^Nh`lhLL8cj@rd62Fla|GI$+F0Np47AoO*8Tg6GCDxcf@ph$Zq$2pci$!WI8=$ zE_XyBBv98AjK^=jZ3}csvM)!>^AvI}0=#L0nzQpsF@lEIJOmxVYfT$_lr)x4FK)&a<;KZf$LG zb90l)WRk5`%h_GANUCZ>LZ`C|RmGng3qVhZz|_YPE`XyA4^E;q&=0Jv|M--;c+~M<>kd1>9~(ux(&4 zaCp^f6>Dp2I6OSWBa+4+t@;1f}O0NH5<82|tP M07*qoM6N<$f;T-q;{X5v literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-81.png b/web/www/routino/icons/limit-81.png new file mode 100644 index 0000000000000000000000000000000000000000..a13e3645c4c8ea2c300e2843654f055e0b52e8d9 GIT binary patch literal 645 zcmV;00($+4P)F20^WHyX6RaIv`FWBjjLhwz5AUhp0 zpBGeBoraNqc07qpbCIgrVzo+I7T<+sk<}`xsx6x4;*(4WnU%@`3k8?M;rm3-42NW) z;IdLVpb#>s>zmAEu4owlCVIv&$V}#ny1wc8^7!}_D=QyRECP}Q01!f;(P*IAY(fZu zg@px3k_1iDAcR0P8hvTn{5;U>MWAUOP*HlU*T*h9KR;(OnPe`P-88uDKL`_&333z2 zB<^EqHbD*1wi(*YCZfMUkc99ep+dEe2%HSW3%N)-*KZb%GR7<#a9%j8_x&C?ob$bi z2t!9HyBJ1HQmJDJhi@eq6q%V32?j;N;af?ij>RxyqLlsZtR^B1RIdj(IQT@Nu!gQr zvb+qD2#k%v%L~+MP%J{JR3{$)!S?o7CMSOm<_F|TB`UgZNFrfLwJKs+@>f}wNVO`G zNLZrlhEyuiKBEV!*8`&KCCTMPOjF(^(-g_&M0C9*^?INOvn-orvwMWZ zOC*`>N;bPEmSsZ>V@YDM2dPy44b)eqA`*)|h+!<*cV1sVGduf%R0=c=0L!u{6bclJ zMJ&t0@Aq?cbwxg(M=3=h5J3B#Wnu!Zuf1Ge+U+}<_Jy@IKcUdzQ3{0unM?-LG&w&% z=jrK*{r!C!jRvQur-Z{{rlzKPVNHW}8_v$0Hk!>4kqCdP)9K)JI*CT3xZQ5r?KZ2c zs~jC2;qiF5zrP>K6Nx~x8FJuofXg-HJw86-^?E6n%QPAdR#sLpO_QyyE#~Ith{xl@ zFqaD)4s0j|oz9R~E|*zcTx5557q8dL$;k=1T#ki>1=8s>Znv8^R{^E4;q%>4s||S@ z8yno+-Lbj3$^860)6>%^rO4;=q|<3GE-r>)wHo+*H=3-kf5YSX$l)Q_Yy;mkO-$2d ze0&_G6wPLn{_6StemoveFWBuuCIipU-$(k-M!W$d-OUlU#((Ow5j5>DXofLAk-0JY P00000NkvXXu0mjfVBRzE literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-83.png b/web/www/routino/icons/limit-83.png new file mode 100644 index 0000000000000000000000000000000000000000..3fc8401d60da4cc0646f7a248fcc9aaa239e2e85 GIT binary patch literal 678 zcmV;X0$KfuP)=h@?_V zblnuoioG&kK+Wc?=z2+VIT72IcgeOzayb!QFG;gG`vOzSA$xm=l1}%f-F_eFRqeJ& zI^CDOy+cvTftcos#N*duS^oxlm1T*<l6bikBH4Qo)I6ZYcXtlybqx`9Ew~O2DCKih^K0c08igY?nGMQv@ za+1f#$DusYD70E(2QC+QJVV~i%?&=Ek8-)p?d>g#i;JwTu2QSjxWB*0<#G+fJRWel zaM9~Qw>#vO%Vn0Am)YLlW@l%ILZJY_{{BAodYwk2!JDfV-QD5$`|*0czd`*zWV7(_@NK03Y{VNd(%l?kYy77^8$r|l1V>0L!sN*wGXMYp M07*qoM6N<$f*1}wqyPW_ literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-84.png b/web/www/routino/icons/limit-84.png new file mode 100644 index 0000000000000000000000000000000000000000..89c4eaa20810da7c9d2c598a35f3ac869a61b93c GIT binary patch literal 657 zcmV;C0&e|@P)6 zKoT7%2*NbYB(vFFCK4x9RdQ^MR8=yOIAJ!sOVdm;2*R&@6_RMCblKR<`6s!)F2#qcmtug7qB7zQ=1&O)IhSgBOv*R{iJqPw=(-Le1a59_u(!8|+uPfAmK|ss;CWLa zL?S>m+F`rCzD6t-gJoIpeIEx02iV`=$Hc@0E-o%`c6Qb&7L5Xt2to(~pxNxOS(b(A z>1k|kZlbTR4~<3xx~{`@UAV4`%gal=Dg_7vyxB9$vbeao$W$uD)zwut8V!+ r@9ID6@&t5sHoLer{?k6|!lwNRu9kyR&`)ri00000NkvXXu0mjfog*j- literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-85.png b/web/www/routino/icons/limit-85.png new file mode 100644 index 0000000000000000000000000000000000000000..1fd523eb751db26b7c9ac5091d6becfdb85f193e GIT binary patch literal 685 zcmV;e0#f~nP)K+E-{R_XxfQHBDWF>iOkQ7ghC>b$gODFi5Ny)Ivv;BUWbU#SG79L;o&EAeFIgU zVPyrPQSkZT`5DS($mgL@sIt2HgPonP%*_1k_xH)B8531CB$cwIR1&dm`KxSOq*M|~ zrEF1ELrgRF%IJZr)oD@Hg5+`{mL>0!Wr^f+BC1-DYIV8?vu&qjvj>t+ccsyIALvz$ zhDbWym2CDvY}<(#MndB82Qkfm1HH;LMdI-XF^q(3_vPg?3kx66G;lZoux%S%*U9Je z*tU(LD4d_4bA5fy-Q69tv$ObozTVKMrl3+$xVmz=b{&o{Y-|Jxhx?DB>pGcC2FtQI zJ3C`+Y>fT=eQLEDjYfk+B7vePy<&$0S}iy|^*E{5!$hO}sdl@K$KxRui{bbCF-?YoXtxKvVzJ2b@-lmSdyI~bQmIs!o10^8ZH>vvNt(?jZ>|EJ4o)T}ZYY-r zysfP*?(gr}-ri3+jE{fh=m?z7zHeC; zmSqtL1n~R)SeC`p(-Xm9kdcv*-=J<6G8t$#zYq1F4S55Gx|>5xjsKKqLrB_RzN06P TJJiyY00000NkvXXu0mjfTI59% literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-86.png b/web/www/routino/icons/limit-86.png new file mode 100644 index 0000000000000000000000000000000000000000..7b367dd3c7f466e767839a28299513744f842522 GIT binary patch literal 682 zcmV;b0#*HqP)#+luAw3)_$AVf} zqE1I7ogPRocPN(SKn!D9lF3`CRQ?V0qDn<1nY zu~@{iEL2tH>gtNq(^GoA9;&LM{LV5x4fVR8^K++jUr|1@vGI;*boeN`u9MAXF-?;~ zp@84-=lJ-TdcDrY#RZ8(g2~Cr=dhx{!vhowZU^mllvs>E)$Mk1yWPa&aeO`>XJ=$TEu2m#fk1#nB7v^!G#U*% z%;N!<3kUr^bh|dMTrRV;w8X)|0bZ|{cDqd=5MX_MovEoQOw;7mRiNL;K`_{$ReCzmN2vjd%h^I-4Wh8vkjZjbPLM0_vwFF@bYx QsQ>@~07*qoM6N<$f@!!xbN~PV literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-87.png b/web/www/routino/icons/limit-87.png new file mode 100644 index 0000000000000000000000000000000000000000..24c576e181b36a47730c8eb2339aabc01e12e263 GIT binary patch literal 673 zcmV;S0$%-zP)xK~y-)wUj?dTTv8;zx*TOBLN9k6f&4i4Dn@%@B*f|y1K=np@M6u zA-fd2I9S|m!J*)g(bpvd?5s#QEE0<~B%3`H!$^p2dtUdtM1%pY)-?P3pV9R-G;M}d z3RD%w#^C7*YBea8pj>XTy!?~x?QhJ?{2KHR%%&L^O*15&cBEPraUA(8jw4d7ilozy zXqq9W8Gmu~MXlDfXj)kc1rf`VcVSr~g@TBtm8I31?#mp~0cobdN$>nlbmPN5x z#N+XBadClR7#trTlS-wCL?ZoSmkS;qpjh;|X}6=ODu2@LcJcXq#N%=Nem|tE((6Ee+M9s?csn-FUs=_YY;ax3>g?K`NCB?RFbQQD`=soSmJqzP^tCRXu(` zc)hr>ZRmD~vPz}G!omW(ySpfg!p+SMr>CccLLuho=Xpc73$~4$Q0R(UZ7AE^+~oTD znysxZ=H})|CX>|bb&|;>KA-QctX6|i=*lG<8{Y{8K5=jWZucO!EDOuBn4FwMQ55d( z?ie2*ClCk>i+VlC<>3DQ$4LL#h$mpAvpK@8@t^kD2sZ6+){7%T-{E_A00000NkvXX Hu0mjf08c{r literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-88.png b/web/www/routino/icons/limit-88.png new file mode 100644 index 0000000000000000000000000000000000000000..f45d4b111d917a92a566177647d29037c6a43c3a GIT binary patch literal 656 zcmV;B0&o3^P)0Xx~h$*+}#+h(dUAp+dC_Tm()Oa&HuobYIUbnq+P>iw2wr-o@cL=LawEdmcmt z*Kr)Lm}W|HxdTZg&LtKTSy&K>#Y7T`bIIin#57alINsN@nust`uQ$uy-bbp{bqr&U zbQ*LWCMMwV5jq{H*P+qqv9|V|t*tN2&HWh7kI1c7QVheCOh!q&EuxhCRZ5Ap+aj5a z62ma1)k?lFoh(HG zDWcIRwOWl@t%g#HnVA{1U%gCE!|7?5lM}CZThl(XzW$DQeDo;QYL!BvfNk3xA0Kml zea-IfE|z6csZ>ZL63oxfKR0U{+~32|k>5kVA4k{uU4y{@zu!+XnIsqtVp*1}et39r z^`q<1@5ep(d=Ln@ysN7#!r?H@W|Nzn8*XoJM|Hp7XL54V4GRRo=fi{Jz+m9=n$0FF zD=U=CWrD#Vl}ZI&*D06FghC;@-7c@L0*-@+NaT!8$K`EqZqn^`$>;MdFE4X=c*w=Y z1^IlQrKKg(>9iZx=|CiMrpd;}SEi;uu)hx;&&aoJ8{4*tMx%s6p+D-8hC?V6;O_3* qSpV6WH(;#0ImXoZPkARIqAr&FyNgRxMFQg{0HFqU)+ymh)||A|kY@REGHa^%J?=0-83= z%nZb0;Pt}eBb3XK&qJY5VRrT>tE=A_9{$ztZ<`GxE}EuGGHFYxBx2k0S8Q9PR1!%h zZP7Gc3?u&HcotPEL!xN~Sz8k^O?ellDYCXEqG<)GREC~qw(XE?c2iQRmegwR6TMKY ziKJ33$!0gjwjGGBPm8MFh++Jj=mo z+cu$4h*GJ<;o%`}x0_%v`1)uA18{cc=lIy^TvwDYEG&eGMA~^Smm`zOV45cT`};JT zO}4hSxVpOH;NXBnB0+zD|8udTz{3OV?YSLPs}W)`{-n`p;C8!-$K&+$^l*B5N?%_e zJ3BkLTrN&dPCDv|#h_Y^IB>bZubix$C;RzU~q7dk&zMJTm>u(2Z6vj<#I>1 zw6w(KtCtK;T@F#l`RR_I_l08yt>yZki^h zX%Y+u@%enz>vd|i8lg}Kuh;u3YPBGffqMN%SN~a;C!njd*~P8#pY~Z7HtlaGHY<@V SmqhUZ0000C1k$=`z(R^(h!W7sdoQPh_{7q|LVDm^4&2}UxZHb? z#G~iBhAb#CMI4OYyigJ}xB^-&HJb$gO7W?Vf`()cr$uun$3ZBYk$@BDAd7e}*OND|b z)3jvUse8s9s9qnFX&z{0MY_DylXQ70t*l6<;F60WB}DXiF52xjwr#VsvqN`x zHzOk>{(P&|D%;!JkNs#As?}$F{c50OvZ}2uztPdrk+RvW=I7^?&*yb=aw17OJw4Uz z?5y(nymGmmYPH(K@3yw2WU?Bd(RfZW>32UqK4xHGfc5otg25n%hlc=ImPIHOVsmqo zzP>)+2kzFV(Rdyp7=%#B_fx49S65doEiGZ&Hjd)};5ZK9aG1ftL3(?8xxT)BfQ3R3 z3=+h3;r7<|4Z~n+YKqa(QEIgsu~>|=vonT&}x0|=$>`>0y=t| h9ZZe?lxH1C+TXNlQR)1epd$bP002ovPDHLkV1m^1P(=U$ literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-9.1.png b/web/www/routino/icons/limit-9.1.png new file mode 100644 index 0000000000000000000000000000000000000000..1e72a3da75135a5c9b0576a92cd8a55dbad06ab5 GIT binary patch literal 673 zcmV;S0$%-zP)xK~y-)wUj+eBT*EF-}%s}m=zslG>{^qVj*EtM3^Rvm6e6qWdnw7 zEM*Ppm&MA$#a7&HWQ*W$5JdcTj`9oS(nx3cpS}T!6h;2_}Z|?(6Pv5~b zW5{Fx!vKbcfahnRRs+t@fl9^3^71!qZ+}KC_C2f*%|fJTnijKJpVcbq`}`xmPgbjB zHtW+gEeer(adbttJx$ZBuvjD;4Zey-gDe(F)2z_8r@J!Wmsu$6GM5YJx~~(x;JRck z7qC#+rSHq2Wi2tCzNZlXCVD{#GM&DsWi2TmJDpFMnR$zR9*`siK>#5HPEJlRFff40 z$w~Z7xm=QnaO0hwzkHVl@;FJ-jXES?KY2( zk2yCt$D^a8-rJQ*q+!%$IL;yrBkX*0bAz$5F&rEmKvh*-TwH{&E=dxi(I|eyFaXC{ zl%c9XBogMSR0>Z|PuSetgb)IqP6q%mI5>#)^>r*PEFcJiURfjps48T59?)uqxuPhT zot?$}{5+e!|LiP5{U$Mc6Q)6 z4gg?cVge&0BfYX(4bZeJi5nYV5RJaa{yref0Dxw*3DpB!g!NbD?bX|w4s(5^S z#PIMiMn^}xq96dawt!~yYhV9apC_QNv)RY3@t^itA2#g|*LFl|m;R+p00000NkvXX Hu0mjf4o)dM literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-9.2.png b/web/www/routino/icons/limit-9.2.png new file mode 100644 index 0000000000000000000000000000000000000000..c874cc0d09d4d0920143a84bdbbfdb6ea10a6588 GIT binary patch literal 710 zcmV;%0y+JOP)2&qFbApwVo=u+fnNZ~H2nVcL%2T{S$ zC5uq%$6o5zp{2M<+pUBD!q|OL?9l$H}Tt_^gSE6Aceb>9jOFEKN+Ds8IMU$H~g`jNf}hl4z@5Px9fz8@_*E z#IjQ4a$uU!-w*fqP_4qj0URCG$>+bZy84k+>PNf3O?KUkEX&ctg0D(N@_qeLzAsfO z(!zo-%W~wpnJ300s9sOXvW~Q|A>H2UuXKAWZEQ%Eb)udX&OoqF=JBr03uItijwE&o$oMe1_ zoRyUo#>U3T=kpy{UmrvwMDRR#cxd~EVK6#6N<1Ft{QR6$D#hjHCAC_O_4RcY7Z;hE zn`2;LpaXk&0M8>#ELNjhZTqoU4A1k}+uLJ$dYW`P&F1DNl}ZHw+qNkdi|p_3cVN{j z#A3COmY2T}jlSa3CkTfDXf~TP8VwSO1cqU7eSJ-DZ!gVelOPBHh{xkZqfrO~c>Nlh s&97bEvo2piS8ub6sqvrktP4r|6J$kL7A)ncKmY&$07*qoM6N<$g4|zLkN^Mx literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-9.3.png b/web/www/routino/icons/limit-9.3.png new file mode 100644 index 0000000000000000000000000000000000000000..d203ff60d745cbff9094134790aacbca986a23d9 GIT binary patch literal 718 zcmV;<0x|uGP)%T$V{Fq_Ds0riap3Z@HP*; zzwd+heIF$8w+cf;zMoU6^g(lTbxlo4nT#|wCC$y%RVsau@8=YT#y@*wlIW|^nB?8N zpZWOlCtTOY^T2W7*)wRjp<0DcpWx`IL80&mZ{GZhZGY|e_sKz!mFxOiS&3AsNKvGJ zRTN2;inOv4$#s1NLH3F94b*5%%5{&lu_4{x>$`M+FKui{u6v|LWAYm;isD*df2(4# zr)Kl}Ku>BmrDCzC_4T)kqB!#XymGlO3WEOzdQuQbx!f1|e%|<{+uh>%^B*adAQq$7 z>k$M2`}_MO5(&n}#sKK`dYqh`u(!8Ir_;fW!@=)%b|l9+jdOeZ0>|l3_w4M9(a}-1 zx3@7(ljGxK0Fuch#bS{{p}@$<2#=4CgOzd|xV?Q5$21|C?EBelmWPK2*4EYtf`D$f z3&70G4D<8z)M_>E?(Q&6a{x;w!89=mL+Et+zF`>5&dxGEKF-C(1-5N-d3i~>TxNH7 zm(9&h>h(HTS62gArvqU~9LuUvt@eG(vIxVFgM$MW7Z=H7GHh*aF+Dv^B9UNqb(M*U z2|Ujmz^YZStXfPjU;aia^@{iJAsz>y)oRgfHc6+`7>2?1^)=~qns&R*&CLy#Wsypy z_-oAY`Zcs#zYlfKhI|1-z0Dz}#{ZOOLrB^`0X4f<3)iZVPyhe`07*qoM6N<$f+Cq? Al>h($ literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-9.4.png b/web/www/routino/icons/limit-9.4.png new file mode 100644 index 0000000000000000000000000000000000000000..50e4b93450261d67a1cac12387cea2d907a072f7 GIT binary patch literal 697 zcmV;q0!ICbP);E!%c9G2v@_Tk?JV5#N`#x21^* zU$*VYb<>ZI2T`@!FWcVG?5uQkrN83pN}8ROY;9YQ5!aP6neTF(SH_2CbB>oUUsEVRC`1qhxUS3c@-mS~ zgr1%rer11upTomL5{U$%PzYi%*x7l(`nrKEYe!2pR24`ny#9}c_)8yvnhP%5vdV6~xL_q*EGtg*!ZR0WS8CP6Dh141200000NkvXXu0mjfP*O&} literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-9.5.png b/web/www/routino/icons/limit-9.5.png new file mode 100644 index 0000000000000000000000000000000000000000..3175f285529adbe3478b77b21619e89260e2e116 GIT binary patch literal 715 zcmV;+0yO=JP)z0|Eki>tQXI=H(^wJ4qc&_epbfBE43{tw=J zACmaxd7dH1$;!6h%QU}fcvwm%rQu=8G{4EV-^+2b@;u}B+73wss#FGf`}PH&KR>~; z66A7_PD3ODKYl>52w%Ry&Q67~u}{2w`Hn>5dr%*cT{k1kax^{d>)=50ef?3sFC836 z)6>2z%aQA5ZW%YAN@Y-%wWEavsb1G#sa}^B79`8sQKd3?1M~fm=I7sNW~QZD?e9Rh zs@0^KnU?0~-^lkv$Z;Min=Q+A{|$62*Oju_vK;4;@v7N;&4UMzv2Ez+pw(*Ox-RSM z>vVQ@(%07qz}3|iYin!l?d?%2l^7ZtA`*!}JPwD4_u1YyP&$36)z#pm{r!FAayd;+ zO{q{Q=;Y)?l5~80EYmbKIXS7hxjB`~<-6`ySEY3NFhsTbkaRkj?$OZ^Jv}`vFE0}g zhuPiT1)x+aadB~hVHlXE8FUQXtWUN2Fhn>EU0s2n$z(V`KWBD!7T0xYHk$xMqfrWl z0{MKNm6a7XH#hHKU0o0k6TxV}BO@b>kB`&a z+e@R-xPx6^gXa+<7W+!E82GVR4A1k}*w|oVVuEBc$>QQ7(P)&dtu1WZCY4H&&*$%8 z#UjLFUpw^l=?CuJd&bfdghBu`8Vzc-8u54>!!S5IJ0l*Cb9s45yO literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-9.6.png b/web/www/routino/icons/limit-9.6.png new file mode 100644 index 0000000000000000000000000000000000000000..a91d27f8f242789ceb03a15b4d6792c391336083 GIT binary patch literal 705 zcmV;y0zUnTP)f1>_8v3J!u;ND+}lvQ+ObUx(s_mJThXAH3y*_j~Yo z??V#5lFKE!78W!&H>ahgrO=loZEZ=Gbr|LH@;;XJ%k)Gd!RF>Bu~>{+tp-4? zR^#~in4zH|Ow(j=aIkk$mIarW_oKvO5RZp`p-|xJ>WWIGg6q0?o(F*Ec?=H^Gc`3u zwOXaoX!Kz5IK*N^@qM_y4t>KgFin&3@o`2+N6BWhoSmH!kH<+Q63ooZFfuYiyWQ@= zuCKxOiIPkU`JL`7g*)!;LKJ@j@ n`uqWX{mnk6#(&DQJ|yiA&#zBb1rV4i00000NkvXXu0mjfVr5!2 literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-9.7.png b/web/www/routino/icons/limit-9.7.png new file mode 100644 index 0000000000000000000000000000000000000000..b3904c939d2dff65bbb6659e686b4e0dd86b617d GIT binary patch literal 700 zcmV;t0z>_YP)P!a3O}Wgb9r35{MoQrh`kTg7DB)rDQK1 z!^Hctbn4Jj=XMXm2A(^H^asQ_#3-4BhyFk?iv*3~MR8`HJw(&G?4bqsf$#EsJiqV# z;C)|^MBgz@k*XSHI(@)Us7_fXgF#Z3$xx`ybozj*8l`E9e`Z}I;YimfvAdhW$w?B5 z5(mc_ryvdBt> zOe8ESib_q3yfR)ux;{xoDKnQNTP?mztrnTfk&05Lu1~(eEK6WEyTeq<%qU09AFK(dYv;?cYGJ(ljy}eW0q&i=TVFFPNVGh;$loxnSEiG)+UXScJ#p z!PwXs0O0QK4o62vxV*eXyfT~ zVc)Oz_xG8}WZ3C+-podWlw}HVy8*A)X_ZJMg6HRFY;0^m(=_yYJ?HNJ{vKy%XINWX zgZH-#yk5ZVh5*w9x?RT?MG-SIGnk#7#l*w}0)YT-Z*O5)7K+6pBuT>D+#LGU?EQuCK4Lu&@A+$I}n1Rsl)6a&dkA8+^V` z*xLgH0RYf$w_z9t{C+<~QAD%Z#Q69)o}QjCJUk4a&-VthZD4Z~Xt%!)w9f`S0Rx@Q i0d9@|w9f{xX@3F1S$qY|pa2s90000PL21anc{+#*Egf1&A2`bc=l8yF z&Uui;FXefLY&)Y|?vuvGPBl0vrBc%1pfomis$A}qY&#>*Gk)(4NTQ`mWq|kZUy;v0 z!?KcOvtXLg-3>o}K&b?U0_^Wsn40>^>(?JhCcn4(TV%&c%d%`OEciM)l6+r(l$ zKo4?VX>qZsm6dn${UEaK31u?h^5p*?Q>k@KD)oR&#T6 znx3B4`T4mdX=`gsqobpmpP$#{%^t54JT@VTp#PgtDZ~2B{Ff=s8@bEB+M1o{8Nx58RcXyZZ@p0DH z)`&zRl*{EdtX>DtBSt=vXSukzU}O5&;_*0!VQ_VIMLZto_VyOnb&17dL?V%U zP_qdyUqY?+rK5Y+;S1>KZFVp<{!^ZHAZdRB)ofQqejHY&00000NkvXXu0mjfTLWU@ literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-9.9.png b/web/www/routino/icons/limit-9.9.png new file mode 100644 index 0000000000000000000000000000000000000000..10be98c07dd8d17156a4d063f24447e981509723 GIT binary patch literal 628 zcmV-)0*n2LP)a+LP{G_un&C858mH9eDC{$ zBwjqq#&CG}31?>?VB2wI zGJt6U;V|&@1a!N=`8m*Ry4c?Sj{W^Fh{t~f^?_N4G;P~qp&;4mkW%uGk&^6m$U;HV zwjBzQo;zkD*NxG(o2*vJ!GLedU_e%@q-{6py0Mu|N`;lm0gJ^cJ@0Lzc|4CS7N@LK z4k)DpI?fg?>z+dVn`j;(NXxpX<7}y)CzBf1*WaU50zx57r&9si+A0=j+~;_mKOtgL**(Gj310D#eG1kdwe7zR{T#lyn` z48y?V<0CxJLo^yiI2^{W4#3_XFdBVZ=$tKh0~WfQ3%nZt>7Fg%(*6VsVp+p;S$w$w O0000;@v$Rg3{I-Qm{Yxw&(qQ-|)Q;NaBy< zx-v~uXRUV2T<(eKG+WjLnN`qwv)KN{)D@`4={`r3I#yZ zfM^tWc>yd7xW5N_JsW3dKX7^Z6{*zE?flj(M4pCWvQqIl9Fm^LKs=8e4#`TzqhXj7 zBENCGiflVc!|1WuB`S~{-9DKyh z4IoK7x#e;R$8nHIBoK{8cSXJrTwDOl<@de*vpsLXUUzekt?{4wY!6NQ3w%f}H(ET; QasU7T07*qoM6N<$f=X@xF8}}l literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-90.png b/web/www/routino/icons/limit-90.png new file mode 100644 index 0000000000000000000000000000000000000000..c86c1bc692fd06f7aaa43fd3987c8de1d3979188 GIT binary patch literal 674 zcmV;T0$u%yP)0L#@K8K_Q3|oaDF+3Eq;DNyW)E4H&w( zDNV>;IyzW%6zx_ae?t&OkencQfm2rp;W}uzNFXtIcTWc~?UfD|(jR=o$Me0v?;F1N z1xW;!>x$I%6bpp|D#|tEadLK+jK@huxn`koKwVGKb;UnxLnP6|wx_VO^9d&>AE0T| z$YcOj1)@>l=?SRUfwME9R2x|MtJeWpz6x=D{SYG~AF;a&2tqG!wOVi-2a}VNh(@F6bUJu= zc)CGve>YYS+#zV-E=^|=H3+M9ix8vkjY^w}im7NYq7Xz;a1gl+C)|?a;fjhChB!*eO+>#qAHcaM_CXUT3g#tE*Gj+dz`a-wWDtA&LH&Cbs1^72yq`}-On zAJ^{g?#tKh?Maq(YEZ3CVp;so#l;0ZJw0r1ZxfA1DHe;Erb!}^;8iROs?|w@XcS_x zmMoLWaC>{p^71mC=V2HI^YimeOiU1l;Y(R82GJ-{d>`)bTQbu$85`llZ>R(a{leb930X4M1OCA6;EtFJU literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-92.png b/web/www/routino/icons/limit-92.png new file mode 100644 index 0000000000000000000000000000000000000000..7200fbddd3458c697ca85ec7557bba69b7f919d9 GIT binary patch literal 682 zcmV;b0#*HqP)mIrT6&EqLgu%MFY+a@9KV@YxBJK zjfl|ql+wkv6Ozg7Ni=#R;jqZ$q)0d{5{=$SCbK8Doe-t;KX)||p{Hs!$kx_pj*piy z%@B)=5R1X^Ftl1wF2mUw6pK}o$sc61UkQbN_V#;Z$BBz++LB7SQYwkKuKZQ5D^eDqo#l;1MLII@|!C(;WZI(a)uC6|Cey-~qn)Zd|czS|^1D}UlZI)P!-*tO?%joDRySuySx{htz zq*5t%c6RXl{ZuNIzIkFXsMTgYc)c(*)aS+HaULHZu`G+r%S#p(7MPitVRdztNF+is zne2xR4T0B-2c@9h?(=kAXL@>?NF+ii6yomgj%+r|+}s?NWic``(hqC5K`A^K#x><~ zpJx~bN-0iGPFPx6VtjlYr4)ri0n4&DJw5G*mCIll*P5)Xe8cbm#P&9LJUzeJY*Me+ z2?PSPS}kg|8r|rPjg8^=`*}M(tgS(_`F)`OY`_T^Xf_Af8vm)!2GF#>02q5IgezfC Q7XSbN07*qoM6N<$f@yj{kN^Mx literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-93.png b/web/www/routino/icons/limit-93.png new file mode 100644 index 0000000000000000000000000000000000000000..d500ab9266f595e5fb33530397ec58981bdf397f GIT binary patch literal 677 zcmV;W0$TlvP)$o)JO)!QNm?N;FfDL>gM2*1`S2X zC^g((IyzWf#dhlu$bTTB1T;68OC*VtTkyIVFA_ly@9ybfO?#z-h4csC^t|uy`-bm* zK@x%Ex*`oD&0_I@$z+#_1UWxXCK6;a*=4bKK*LDWb;Un>1(FzHS+m&P{ea`+cTm+h zG!0M`AQAx{AAwd2sMUaa-9j$+4cptF5s!Z#^^eS^nWCy1%;!Beo22LQuXrBWY?ArB zM^!awnyDAZv&gb$sj7AAIyo5dRSX8Cu9K=-r)AAP%RDc{a(Rb^f=}Cio#+MICJP0h z|aCMG5z z%Q6~`1`ZDo;W!TB@i>H^wV0j;+U++uJrzMkX|qxZTy}nbPEFIey1L4hl@(U2Rc5nU zuC1*xlgaSx>@4`YN`+LEb_o6c5)=i$b9s4*si`UK@9#qtMO<85V10cZxm*sDlaqLO zcnH)(QGkAbDFjIZ!r?%cN~LgjcZX7`ghr!*xw$!HvsrXH9o*jDLXxDQEF12-w`*#LUbLG))W2S}j19I|4U0z91TXkG(x06dL8j;SjcMBNmGx5{cmc z{vJ0sH;`o+(P;FS$oGM*EnqnOI@W(S<_Q?shj{HJ|3hE4ke!WBw;ti9#%00000 LNkvXXu0mjfrk5*K literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-94.png b/web/www/routino/icons/limit-94.png new file mode 100644 index 0000000000000000000000000000000000000000..975386b1b1194acd04d88f3cdc92a5dd68f9c3c6 GIT binary patch literal 677 zcmV;W0$TlvP)pZr`5Nb3+xP{?2nnM7}f6z-DRDc~k960Cu zkVM<^ypU`=qe5X>!^4M4B&2}>DUpzdhYwXKEX%et^1RUF+JGcll*@fAEWBrP^9`2O zPc{pt36Tig+<@!Cj~}qJQ|9&S?|k_1h5r6ZtG;D+oU|;<*5stGy*Y!5cs{9cpUckpR>IkLZ-Q|m6f)`c6WD`&1N+|KCWCYr<0Qt zRjXBvj*e<#VnY7o$5~mCOmja-wK{}p@+YNIiJqPwmX?+<41;2^$o%{~7Z(>8hVgK# zdrT9m)uABaFm!dbW$AR9%galqr>AjUm$kJu*4NibCX<|=o^o__)Gq7lf^e81o(H$L zZCNN3BAHB*N~MU$;|vZCGBPqkrBb0%sc?37)-Jof17l=Xq>x zZ80`B#@yT--QC@+uCB7Nu|Y1EYnQn$M56}*O-+5}*|WDSEEJh>} z0pRNDitFoZVzJnRsM&;f@1W86*3o~~;ST6%Z+38M{HJ-=fl2!l01Hhj-K=-V00000 LNkvXXu0mjfAsIw< literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-95.png b/web/www/routino/icons/limit-95.png new file mode 100644 index 0000000000000000000000000000000000000000..de7f9ffc6bd39e9457d863af6235083a31b30dc0 GIT binary patch literal 690 zcmV;j0!{siP)l1N;MWo?OVr^WNM-+L7ip-H(sz}nge4i4rp z%|XV;A(;eShv#P~mEh^LbgO+>#vs{jl#u6H^;}CwxTGXn460*Hr9L;$8pH# z^W5IvvbVQKe}6yA%gfx~-{ZP2nM{W6?(WxOMS;gh*x3oGJUon%O!6z&*Vpv)^suq9 zL3?{U+uPebJw2gm8i_=LzP`RdJ;@|IJdCNdwLv%>$Wo~k)oPW6g$2&e&d_z8si`Tl z*({rzn;ae<24&$ew6&q)c~GwhGELJM9UUbej}whXF${xPEXL&IB)z@8)M~Y$tX>Dt zL&Y#IDU|}5VHkLxN3mFBdU~3fnHh@3B9>(lkH^Vovq4#@1cq^`$o%{ly1L%8z7DF| z%xkq8uImzsM9_7eN~J=zS|t*R(9zNHQ`Bg{;v&>)Ut9XmT08+Qoy`_*jsLXITCiz< Y0DKo8F?gAJK>z>%07*qoM6N<$f+7A*Y5)KL literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-96.png b/web/www/routino/icons/limit-96.png new file mode 100644 index 0000000000000000000000000000000000000000..8898cae1c4ccaa491e85a3b737633b9675fd4b66 GIT binary patch literal 684 zcmV;d0#p5oP)&-+`PbKVCL z!FEj3DY~ALZ1zB6u}g_WMCRs1A`y{T>{7DX1JU)An5Og3Sw%z`sM!p$yZeDsX&p@q zv$O>9IC#DA^aRx^oSi|r++=0tJNf))!r>o-`2pE55~69kq|=sEDk7F8f0bp4R4O9r zv?ZFRi(w>Q884t_GbEZ;mSRz)(~&o+(-A2aMKrA}&1UEYW?2p?6m}$&=}Wu)Hqfiu zZIMi-FNMO6Se66P^+idgZp1MD4fHC*5J{zOMAsLcTfN>#=I7s$&4Qu;(C_y#41<%C z6Wne$e!rhaqrvg^TCK*##RbV^lIiK`m$0J1{XHBWx*fDyQQ~oaSEJEja&nUW{e4D8M%dfi zvovO11=X1OcS1;ZJyKVL{(K578VEwgG8fICMG6WU0r2nW`<6uV~0IIgK6Tx=c`k# z+B~1nhiRIeo}RL{wnm{)V0LzvTrP*Isw^)r+hNrz_D$?d>gozu$gm{XT4NLbv;EsQ+xp8!*(}9Aazyr#>4()BXf3VIR$f S^u&t*0000t0PnXj3W;v9a-)qoa8& zYnWsbOcVP0;OPnSc{n|VT&~2_)KAjsZwwFrYWKIzO2x&pY?+&L<>Erbb>*+PuE@oO z$lRPOmSu}l@fSx+R4NULW#wdbRis*#cTug1tgeb!R!&N#p_a^bJ(9_+%fdoaDwX$% zUZ_+=78aV4$*ha(dJx+lmqg-Dl=?T(3rdM35_e+TXNT$OX=Z0RLfO$vnq6B85k^z^)yeaG+r#O5Y=Jng(zt5K;`2!%rU zd_Eo?9_a7y$M5%dikeMWS%F&ZM_2z@mnWdBv)RS1@t^it7dGv0z|bHLId6U;00000 LNkvXXu0mjfA%!`t literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/limit-98.png b/web/www/routino/icons/limit-98.png new file mode 100644 index 0000000000000000000000000000000000000000..b6aa0544a9b968f85d0da3be1a445bda25786a6c GIT binary patch literal 689 zcmV;i0#5yjP)K~y-)wUob08*v!MKbMOn8f?H|;tw(?A_y@gLxfvW-2Dq;z|cWQ zX+n0Xc5$$D62x}vBK`p`5(!EUkxRfIn}{f>OSqFrFnE`@gEehR2Mg%~-|2bY&+>iW z2N9v;*|sXWo{&stTcXi(35P|-#zewlk!bW>GMR1B^@P~A`sb`7BD7Vl4zscGoZ&3Kf@fyCAr+Xq|+@i&DVim zWSS!BbW3u%b#WXQqU$q~NZg2F{2S;+h9Qzj+=#Bvs4GuT9~d8hOC|$~0zj+P!Y~Z> z_xI`P>0w}CfI^`_u~@`$9EOI5P=00!h2Zq`4M#_+x}qo_nV%05iL^h(FbuNUEZ5i9 z?CtH*Xf#+|UBxs_c6N4%Mx%_5jy{JK1s)z?celqytrj5`<9A(LT;TWn+1lE|?RIl` zc!@$JA;y zdV702VIB{--MFxAXf``MRaKdom|${pl29nb-Q6A2)6=Z2t>N?exVpOPgf*LB+qeh> z&Zty6yg(p;ZQC3i956RG$L8iHmzS3;E-o@PHAOO+?1WV+5D1(pvas-l{{DA-{tPZx z+ppK_n5IcE7{u%KVp$fJWidE7h|lNy1!}cmc^T^UuU*}HhaZXidoUnSQiYcW4; zL~qw-bgI*VmYyo<_Z1$K>QBj*gBnGc$vFy$(&&&}y|J`=RSVzn@W{ zX+S(4$#S_I9v&XBy}gZgyNygH6M88nMx#*_7LNm(1`VDE42O|SRaGo6FC&>u;_mJa z>2w;d>p}>D+uK_#E-pr4!y({#P$0x5y4^@7gn;LHI6FJT=H@2W*Vpm%^b~r-Fru(- z7ZBny#;vU{n3{Tzg9AWO!hA3oz;PTTlSw2Ji9dX(AOLoDfWhGFSpV6WJ7BE6ImW5+ epXS*ZChZT5T}hNSnfmeo0000eVzjI*i^W}LG8fc!a%_y$buyE=V6nJM%gQncVt>~PB+--Wj$(WJD-I8rVVWuA za=_#yFfaf-Jpqmb9326ThKsqmD%RJ3AeH*ntM8F*dy1xMF`p0FY?5Kfe<}>gW|Pe4 zLz<>V+n#!5yntMHl&0BWxlFd(e3#m7vRo!jvq9G#eSw9c$Wm#Og+iB}_dd|8JdZ3C zx-6A88HOTgSu@OLZ)n>e1HH<&$!zw9mNgUGcz*tdbow)jML-bnVU3IcXJ-iMA;Ft3~QMMMx4L%aP8*FoaMF*^$C1nBqAYTkB`ta4Y#+qP!t8CDB}M99?4`9ilV%Mx?Nyx4S0C?+1EYm^9A(v jHv5X1^@s6-qmI800006VoOIv0RI60 z0RN!9r;`8x1N=!uK~zY`jg(DK<5n1jkFT#|$2ft=35oLQh6qYnbXatLM62$mLS1&> zRaL1-?QS$x7p04?X}hWDqU&nrFO0evBotr-q%l??F_!Wio69fS!<$2zmVHlsDo}PYVn&!uNK=F9|pJK82lPt@Bvn*>*&X`H)U@%xwRdrL> z_0N6ZpA!TDu~-a*kkI4%KKlJWMxzl3A@<_p;$Njw=|@o%-%dd-%X)HfaPYfnnolH2 zLN1p>GMNO=^I#Yzga$!?@pz2O%S$wyO<0zNcs#zly1M$MBuTp=bTAmK)M~XqEz4S$ zWf|pi8ImO7FU^vgo-Z7>XjSS)rY=SU<105A+AG8&DF z#5B#Pf*{NU0)VDz=yWBH4IWtp+Vc>Zlfd7l{x-M9jo$_@$ z9rSuV5JEE*KwS@VHh7djssbi!)zFa0mCpjKR-ve+eN$GhA4{2=ksA-ec#8)$q6nlF0in$ z@OpW9`71&xy(*PTFXHj|8^bW5s_LCnq*5tVDiyFSi$o%UrKKfAqtS4#>pEJk7I>aN zuT&~u^E|JG4~k(JpX~4N{}BX1Vr69o>2w;8X)qXIZ*LE_ZTpo<<=bMhxCI~x-!xg4 zUzSRxA6(b<8;!JdZ>o=d+fXS@(FNG9T7zwH?c{)>EkzY}>|QFz}a_mcshX+9P;}Zu0$( zQhKH9I&9m9EXyym+3c?m0Uw3lX1^@s6-qmI800006VoOIv0RI60 z0RN!9r;`8x1CvQaK~zY`m6gG38fh5DpHbSurbIUsnzUU~dzndl(Slj?7qpt2vdP7O zG(F&OCcC6HG=o z*#};jZ@%~aeellny#tcyjYgv}luoB-d_LbN02~xPpYKyTot|kl8bjw90!f(K+S-y< zR#q0%>GbdIc6(@id>me{7osSh=Cs>wsH%!;wF*%bo3pdCe=aUAu1J!!b(&<-RaNzP zJRTJQWm#q-kzl1#VW-n!uR5I$E0qcpi3DX?CIEUo9#vIUkI4c_lu9MZ>-81{L0}*d zpsK38j8s)+AP}G+2=scrg;J>`kp!B}=Cx=v`VIht!5}qFyA;rrrfCcYg9N~6H2SXD zY+geum6~-p9EXx5u}~=VuWU5PYL)!{oz!*9cA-$9BuR8Q9EYh?Y8I1|lm7rJ|Cq2{}4Se*9=j zE0@c3yWOVcz)&b;`N|~8A3w;456S1xyX#tweDHvL@xqeY>2w$hg$RH)01!nHcDo%G z9UTEbe**XJb=Nm;0C(>KU%mi6{ssWM-G1t48vyL=>|l3y7kxznSFV_qz}2gOu3OSt ztrqt7_PS6}6p!-xJl5COtw2HOf({SO>(qU|e!aWbqs`4tWV2a_qIiU*rKLY?Hruf* z%T`OZTIA!$WH?N=+hjIN-nvD;d2=SMR;$tP_Y**8et!Nf_V@Qk=H}+!4>bRW59H08 zUHJX`k z>MBK1m&>I&olYi`NvpjK!}6+FESf__kHun&VHm<0s9_jF zEEZD$=zaTpPW5`7kw}C97>PuZ^?KdK-YI29y&av?a-Ly$pi=04utmhR}ch-!(mQLOjzvmzo8_t zv9S>t85!9(f6dd=(|>82_RD|OTnIG`Ls(v3et9y?%lUl%w~K221#*2Yt^Bb#Qvd(} M07*qoM6N<$f^72J)Bpeg literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/marker-1-grey.png b/web/www/routino/icons/marker-1-grey.png new file mode 100644 index 0000000000000000000000000000000000000000..ecf8623df601529b410cff06437c16f5d67da4d9 GIT binary patch literal 963 zcmV;!13dhRP)X1^@s6-qmI800006VoOIv0RI60 z0RN!9r;`8x19(Y9K~zY`rIaym+eQ?}|2myanW88IC)? zS1=0HKsyu&QgmoMWOmU_89EoqS4cY~iy#{YWRZd#i4ddcOwyJt9=k+>Y)h8W{sAuc z-uvCd|Gi@(j$=$KilU6?d0#n>^F8PMOU#j~s{a&=#oriXeP~Z0-*J`ytIOmTQ zMS-sC$mjErBngQ`Vgilh7=yt8-EJ4HRtvuGLsixH+uPgUDT?xb0u96PQMFoq<@^2~ zV+@vMK~WSelJEPd*X!^+4~n9^+u7OqR+i-x5=D`0+x7wHe2+1Pa=H8LI_3I^?N&X90&D!eQw9Pu0sf+IF7SVIOk8&=``~B{LF%0uZLQ#2FGzQ8ja>& zn$PEvPN%^+e?kBto6SO!q?yS<5a9aydaf;FNs@$YHVXhC06+-2u{P7vbsf9AyU68o zxcyM3l}IEe`%C~}Fc@Gs9L`N1fA=Yhq6vc}lgX=ozmIOWi~pluuZMoW4?@TlF%08H z9LJ+(vkBk#{}XUH9KyD32q7X((<-D`EFNl__GU3agi`vkR4P4JRrQ_cd9ZDJCN!7JVP$3I&bjNlXt&#tB#NZZ$JUWg+k#-)3nMWe*xh9{2c9e8<|Yz*fh=Ol+sV* z*fbQB(od#oF7%gkj%Kqt$(LpMcsfpQ7s#@Fe4oF=;Si^%rx*+dv99Y!>+9>4+l)KV zMgBIM&GGuYH4NhrfOrsep1++=2d!2MsZ^?Gn&z{mrKJzEiWB$!B*HNKqFSw1eBa+& zU0p>G1PH@WY;JD;P%IW-jHmt3PN)6-ky856bzKBO0LIwsLZR^cJm5m;XMf)qhVc>r lmY0{$Ow)WuDec`?^DiOg#BZC^Vu=6%002ovPDHLkV1geq(C+{M literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/marker-1-red.png b/web/www/routino/icons/marker-1-red.png new file mode 100644 index 0000000000000000000000000000000000000000..dd50628bbf921b40fd0fa83238d45242fd570a4d GIT binary patch literal 902 zcmV;119|+3P)X1^@s6-qmI800006VoOIv0RI60 z0RN!9r;`8x13O7XK~zY`rIo=?8fh5DpMj(kv=P>m9u~uC#v60M#Y6u9@YEQ}!7<@v z553O8o1Tmb-5U4@Y8JDB?5T0_RF4+g1K+6`ybv@7npkH{3Rn_mo;_F$77%y$N#0~K z-~0YDdA|332P82VtyXKgP$;~JL?WL6a9l(pkxzv};YF*}n*Ny~kwjmor>Dy9?(Sxx zP)KyU-RXse1!$TES(fi|y4@~>5I8$KgDlJKWHR~J=H}+Eq9~_#NqxEyA{-8f1pun5 z%4{~vdcDqGug5|4dOgMa1q<8fM+^(bIpmSr&>j}rh>snpwcyFG<`KA-gY zeBTsBVWmZGw|gL@boDj zQZN{VrfC2G4*S%y?6cW0f38(3pAU}aoNC=CxFNE z;Foqf9b8^s-a=(r{$4JZad>!$|D)sMW1O6vK$hk29>XwxcO0j;x3>o&#D4-dn@#NO z?BM3+##vii`x94JSF`Ku>wjQU^CuGI^XEgi-|2L?v9Un_TwY%OaDION>%B1rK*KQT zjB1-VZ^+lLN!PvidVhbPvMh6ce!fvEm7?s=K1mdd#dshPu>5{MbGh6w)OCk|rfK$< zijz*K4cB$0dr;SPrF1%N0B}@QeRyO^+O|zy*9m~SuIFsq4sdWw^~1L9fUfJgN&dR7 z%XB(T+^$K}G);Aoa}P=qrfI5^{4JGAoSU2D%*>3P%jNW+8AH%X{x%v7Mx#*za1Fyq zJC5Ud6m*=wj^ofU3<6*@8Z9*%jrmdaBcY@Gy}rKY!NCFjem?_&fK@COz9XWCPH1;mA1FHw{W}6YvpqJ cw@Ees0LzfXKl)x52LJ#707*qoM6N<$f-_~V@Bjb+ literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/marker-2-grey.png b/web/www/routino/icons/marker-2-grey.png new file mode 100644 index 0000000000000000000000000000000000000000..64a954af61b7a10b62133187022b8070cf5f83d6 GIT binary patch literal 1091 zcmV-J1ibr+P)X1^@s6-qmI800006VoOIv0RI60 z0RN!9r;`8x1NccqK~zY`jg&iY+g2RK52-6rrYPD5Wj$z%qGL3U}ySBwwNJkN}1su?R6*V?mbeI0v}X zJ^$Zu{tqUOV~i^d!;EQ~pPrtcenTn!7`I3i#s3sV`B{>rzY~dsGycbnLtWRc>bkyV z7{=#O6lK!sG_u(&Se6}mqA0@o`8kHeAy}4m^7;HSy{-tt0hCDG%^E1OR9teT}P18UKnJNy$5J3=tVHhNn$!l>CLZE3HE-o%$ z7{&uhlAa|tH#dLtJa1W5RpfHHDIld3M@L7{bsgPq7ryU96vb=JOQli(0Mj&6!{Jb2 zDW&(*>GX}jw{PF#vhy>wb87epPz$LicBVhMxz1Xa!u#v=AbAF zfXiFZXf$A17Ut&W{#{;P{(&WgyslI#k3>;?Zki@^UB8xTKA#8A^I#a}+Pjir80dDp z;5hEp>gwv(9LKdr2gNkaPxkls|A^ywzFMuKP$=Loxvq=d-Ca126RobUeyb?TV*v4J zEhI^LTB%fi41yqPHk(uVPAUijG@DI0j)P*cxFgH*Hh>ra*fC!$%kp-ySlqd~K0y%N z3ccE_OeS-vs_G*`$eXLbaVQ8OZ&X#iG2h8CYqeS`XGIpm*ilYPDcl7G`H>|0$QtPXI(81idZaUayCCyNzTr zIZ##gVJek+byG1jxg8TD^U>bk-i~eC8w(2ya2yA&>qaXpE2HtbsgA{a^e*4;2qCWx z!+_&BkR<7;EX%*%2D}scF5hS6a`_hkn3X1^@s6-qmI800006VoOIv0RI60 z0RN!9r;`8x1DZ)hK~zY`m6gG38hI4Q-^2|+Y#_QhB`qNcr8Bv-dN6BRLMeEthLU2s z@iGC)A=h4pl9I#bu!ofCMQ^q&4Qmd{-a^bjP>6014iw6m9z-on3ngP{HAUjgcMqdk z&5WDugLn8bzxUzy`@Zk*4FRD3Y&M%?xm<1~7!1B6A`Hu5F!(N)%dIq<&9RFN4gmGy z=;(;w-rnBG<#NBa+wHNLnHdTM0>l_Q-_vflNl_H4)oR2TYsKU7zcw~Dwt1dEI^Wcz zD~jUx`~3%DwTpD2p}T({eDGJ6n~FJ0H|Cp^MOF1 z#Bm%Vkq8t;!Bs+06htBsa2$s~AW$lo%RB(0R;zU_kx0BDB1EH6=(>I-U@vrCM>HA* z5h0OCylJ&s*C?CK#@%lBcb?}_DwUiozkLI0HQ@7S;Oxxyxl}5F=Xto@?(f-bHcoSM zbAJ;NlF6hquvi2hJ_M$xftxphXU_nK+a!}o5D~gOg3sqerBbm2zkUVo-UaU6175!d zo<9e?Ucli1l}ZIZpRZ>*LM#@ubr}Hg;RA5v2Jq^YwQe?n`}cvB6~HuYu875AAR_!k zM8p^)m&--AxPF}|ohG_-$66mA5`Fwg6b=({+`v|s%QfI61_&JMA+w+FY|4aV3H+T7gy zePm?hOb`USrM`TzfDax3@81KR&cE3gnx-KX3W10^YinzN(DCu{#OmtmpN{6=-v<~2 zZr=tTKL#E>0-{mt#N9@no}OZTeH}!E`T6<7dc8h9@Jxvak|e<}3=8`9t<~u#Pk>m= z%B3aX<;!#E&dv@PW0;zn(h7w_2)(xtfb#i#)a&)?9*+l^OvZwmCSVx$>;hF)?F|(} zmSxE_O>O{cnkFa9vP49NqrMlS(P%&vMGz50QOq5| zjP96JRaF)GIRj7tq^hdmsPBNw3YeUn#Q6AlBa_L97a2C_P<`$8F(pZo4Z|3@5_Fip zhG9UGBoGlop-@57v?-^0N2rs&CnqP^-Q9)9kjiuu~_`&vYLMY?mvoa TbL_hM00000NkvXXu0mjfU$@k$ literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/marker-3-grey.png b/web/www/routino/icons/marker-3-grey.png new file mode 100644 index 0000000000000000000000000000000000000000..63ecf742a76a48a86c32c706f4755d9cd799ace9 GIT binary patch literal 1110 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 literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/marker-4-grey.png b/web/www/routino/icons/marker-4-grey.png new file mode 100644 index 0000000000000000000000000000000000000000..bc310ee8acabca61d9f4895e7f70907aa242a3e6 GIT binary patch literal 1029 zcmV+g1p51lP)X1^@s6-qmI800006VoOIv0RI60 z0RN!9r;`8x1G-5>K~zY`jg-M|;#L%ff6OH|jxnePg0Kvh6KNVyq{*W55>X)kSAC>N-_->7wgu<_)S*7c+|>5|9YSG6NPK#uwX(0oz@)K_r1p{@p$2 z+)qdUJ?AFkIL5NVFx<3l`}1C}_Z_A5Gpvy)ivLt9m0u)D`YV}Cdds=QGSv6|2fD5w zT9)-?6h)gH$03u+fMJ+LO%z3#OeVOzyadBAUa?sGvs$hG%=7%&EvW0d51Y;A@08Mq zf*?SaW#sdDuq=y2BC&wRaf~1cFdB_unkHP=g(!+|cXxNc6$Ih!0_ywzgGQtAhwHlg zk|aUXGzfx#kI8jibUGc_whciL-qh>$uX&z7W5O`xbzMKClGf7Z#-zB zub5u1cMwI9Aj|Td9nNMmG@DIyI-Qjr%d(6piiBRTcfe3eA8{Oqd_KPx7{@VmUB_rN z!hAkoS(DG_!EqcYrH>c@$Ye5LS#~XSI2>X$8r@mLvMe&0%zu^u0K+iX#%5WQ$pnUB zpja%z^E}*KmMbL^iN!oK01yNLrqk(4AJ5Ov!EqcEMY*;FHx-8Ag25z{$xFv^FdB_+ z6^_SajK^aHL4a1P1;=qfDMhQ*T1>)nt>ZXg80L~u6y;eQ$8*Cl;JWTDp9+Nn(&;qr z(sVk7uIq@RC@hsq$BEf&_DQ?le$nsuzbO<7sMTs~XZ7Ud1aTZ=Z*OlAi=qhab{nQ? zBA3hkU9Z=_VhADctJUgLQ54_Uwhdj^*LIZ6W|7O~uEi|N!eB4}%d+owc6JU}mNggv z;CcQ`)3hH6A!E}tF&qw8LKQ`Uq9}{A@B7eo9bp(oilRIhMe+6G^#MSVq?grd^{4rK z9(B9jTmIhEd_G6F+lA+OD3wab<#PE5Kzt1fATF27N2OBf`0Dprw><#R@Aokn46wPm zd8(@FQ$omxtK7B0BZPcVRrRC(Qc7VM#-g6*`P1e6t!d_Y{`4+?r_(9g?KXlSh-Fzm z+S=MWzR6g*B6s;~7zP~2K{lIxttiTK0MWgm>--%KhcHbOsZ?sLs_K)Cjg5D!E+^J~ z5CGu&{-=#bezd*4{e7iUd3N>M_w97q-yaAe?=8!M=XsDM>1DZG z{%sxbqtO5TeWfVMuKX1^@s6-qmI800006VoOIv0RI60 z0RN!9r;`8x18Ye{K~zY`m6gFy8fh5DpOLi_G!fQAqiq*MdYJLXB*n!)0qmiTRSsgp ziS*iL4xYN6j0xQmxOuACF&l_CyZ9$8Zj&;{!3#lS;6R-*DPTyLdG?^VP@uR^@*d`! z_x;V}`QGpSKoY&tXf&pCx!ig%82k)?qaqj#e$M4`>y1WZ`g?{z5?!61o=UsByIZ+j zZmr#JPcJMiKv5KkqIj3nZnvT9I?m3{Ac|r$8jT)qZEfvJl5~2P)TQgX9tZ>iIsnSD z%uFW3N~OY1r^8-#IvrLj6=pIS%CbxV3|891c_0b>2t1 zt}`4CQxF6yic&0Pt^n&!QLU74oINF+i4jK||2o6Y7Fve|6Z z?RI~YB#Fgh@yCR(U&(rXV7XW?(O1wNk-Bc*A;r%%Achd@sY z_R8n;K~WR{z)t`miXvPt*KpvMFF+~j@si9~_`xV*gl_xbtxFMY=p0993`J)~tw zk{>>h@86S-bGNj=zfVyVIX^#NEffkNcF#UZS3-JxP9 zlS$Qa9H9^DIF67^CRG5oEX!k$tYuk@#bN}&SS+SlmgQydl9MIrj7I0aU3R- zN#2f0!!QiFm(vF&3BxetasCzx1M9o(7YEwse?v*)`1m+HJ3D*Xy_#26R{k literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/marker-5-grey.png b/web/www/routino/icons/marker-5-grey.png new file mode 100644 index 0000000000000000000000000000000000000000..0d805e54825755cbff053457f5664f658e8297d3 GIT binary patch literal 1099 zcmV-R1ho5!P)X1^@s6-qmI800006VoOIv0RI60 z0RN!9r;`8x1OQ1yK~zY`ja5%?;#M3Sn+FcYCNzd%K>&v!Lda!v=zfS+y=@OY^w6&m zRcfU6P-(TR9=dzzIaM!Jdg!^@{R(XlyGlfo4HCgP{IhVf7~6n_?H;;y0VkXN9iL}L zKh4a0raaHX{5Xyi(=_eVR;%^3uInG;j)}+Pf9Lc09|b}9BNPgm^WUg>QOmNnWm!H@ zRrPb%bz>_lE8uw^G)?;$*L5)(jWC%^K-09jzP|o@p-}jNW!X0YQNu7EoSmKhqU-tt zj^mKcW|2%L!7vOcitLK?uXlEKzT!CUwJ&N})^@E{`^_+n zJwXsqEEd6W9PS&#Fwkf;plKR7j(fGcyZZ&pvTta|aadWF4|HAM69fU(YW0Hz0f6H; zRI618f&g9D_heZLWoAUXoU;wePwvm(6Bx-SFz_3hj1#;jCOPBbiJtY@E$zQLopzR;#s7bX|WKjYbzh z%f5xfVQg${_(8>Du>dZaOhQo<=(_%p007VPU>GIO6oe2ELcsHUz#;%( z7zRAgV>}*%002$X3$>XS9goMbEDO4>!*LuW5($*c<-igYMg8wH08FP-*tU&GB(eac zX&QncfaiH65($h(Bh>44bh}+hk`xHkw(Wm;p-^Zt91bxU3<8^mLLn3i1&E>umSvGj zrNHw%48sUuZ*Fcd91cO#^n}jka?d=^n<bHD2hiUpU*#^PN(@!r}Jf_(Ll9Y^@E7TVu(Z{xVX4Lx7&qfS)geesZ`1rbX^yh zmzNk0he#w6e{F7VeoqrZt_y|2lXyJ-O4BsRvg{Kgkq9c43No1tD2l?`+8QdA%0i!2 zRYkAY1H&-gQmM4hFpT0qD4M2ya(a6DtLJ$iZEtTQolfJvu`CNGCnqpX(=C-s-{kZ8 zX8=6^O%nv+MWIl5I-AYhX0v%~7|V5QHk&X_6QU>{WipvV03HC)b2u-P$sCHJc=X@v zvwY|0r_<@6*Xtn`i=9i7^n?)d*016`kPz}#lB8SxU2cVD0?+vxn()`e<1rAbOv_ilSgR9Ab5K_0L=`_Z)!xL85p0 z+wb?$ZnqH*hi@cFdK`&Fy0?+3<-h4tWh%8=?Z_~Uy;Ld%(==gOmb*gm-`6-qS5H3BuS46Avf>i`~xh;^*)cm Rmr(!!002ovPDHLkV1m~|`x*cM literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/marker-5-red.png b/web/www/routino/icons/marker-5-red.png new file mode 100644 index 0000000000000000000000000000000000000000..e6eeb9347ed2ffc31d507dcd2399aecc60c7526f GIT binary patch literal 1002 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 Q#{d8T07*qoM6N<$f`8ZI9{>OV literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/marker-7-red.png b/web/www/routino/icons/marker-7-red.png new file mode 100644 index 0000000000000000000000000000000000000000..cbe9f417bcd0802dcb60e7e079805570e379cc96 GIT binary patch literal 954 zcmV;r14aCaP)X1^@s6-qmI800006VoOIv0RI60 z0RN!9r;`8x18+%0K~zY`m6gFy8fh5DpMjVOH4)a6##lponDNjQ4k-Ku4R~mR*>V{Z z;=!JJn}dmm^{@xqIYAGtZ8tTGO(1(Si+{r6_P`{)fER*9@L(KF1RCPZJbM6K5K#Lh zZzhxPeSZ1A_j~6ZkVJnp8jZ(w0E|Q;Gz{ZGz@7}lU?dVD0LJ6-FU@B2 z5z^^&%;j=jNs`2TK0oL%O|n`gE0w{hR>|w@o3wmBPf3#Ka=EV3>2wTBOH2O(U?Pzi z-bS@bE-#aFbL9Ly`S@|?847jaeM%$}1i&s2^m@H4l}f|Vt1I&BSMtLL^5aMH&6^HX zQOHJPAhlE~(d+g0S`LgxqeHhmB-Q^$K|UbZFV>bl&u{0{~7>PjPm3HXdkMz~|4v+#C>%j!bE_S~x#H@4Sej_+w&X zg1fuB(2bL1tJUJh#s&c}6bk)QsZ{0%K2rcx zRh5=y{qL&R$=9#Rckf2-a&K>sq9}59cD9zw<$~;e`y`RgW+NVt$8fvdOr=tTPRr`F z)GZYXh2BuHG)+@&+ZG0(wrvZVrl|m|JL`LMfI1aLApk0hlB(D19`;YEUexP#kD@54 z`-Y2c+tf6T-7#4x6bf=bX8=kPg+f6dTi>p7xg2L^W;iu9RZpc-%5BCF^uGG`wU4c; zs%BZ1<3Z4I^|dUEs;UwIgTY{~R;$g9@{fd$s_*6HCHMFD>2|y6@pz1EHX9k|9SiNR zuOJ9qU0vnk;^I*I{4|s#4i68*)6>%zyX1^@s6-qmI800006VoOIv0RI60 z0RN!9r;`8x1OZ7zK~zY`jg?JL<5n1jk8dtzy4CC`42qqGV1d_=l2q8mH5Ck|sKS!_E10lqjnVI>kTrU44Nm4BWwQc*s{{H^& zobv~aG0e@)A)C!Y6h#PvFocF-2-kJdZnx2DwP4#eQmNFd<>lqC7-O%7&~CR|+1=g! z)3)t(S(c%yDi~w9PquBNUa!M6O)$n@uB@zlAxTn=_`WY`nzqR~UzcSW)oS&FfB?W4 zL$z9kEX&}WuWOpN>HEG!P1F3$FpNixF{r8vN@---^E?DW073}H#>TGAD5X$U6&Dv5 zFbw07EXyxq8yg$Hxvo2}Ch(Heu^3cUMJAI$qtO88JmSq}vq&To;G91s0FX>3A&O$e==(lA&%@;8 zB=Y$@rl+S71Oa-zUc@VkB9h7E|B(OyA>`UQqsFPJDI6akgL4kYaUe+&(&==>D+t1{ z&jbKm*G0eIj~M&?K3vxYr4(Tpf)E1F^Wb^jtyf(!NGulXS(b%%yB#rFmWA{4b1W?_ zVRdyCYinzul%myYMZBF(2bN`l5Yi*XV)1Dhh67#KVcT}(q|x7_-_46OO+yd_em-9RS)#|XT>2w;5G1O``FvigD_u+XSW@cuFz#s_FXf$A17BZR4 zzw`6+KM+dkn{v7QB$Z0NG))tlrd{h_rBcD{>?{O9Kr)%c^71k=nat2*7zR#HPa%rp z>&3;zuSHSRhYyNrnxE|L?fnsk;bf&!K`xiWed>0**xA{E<2b?M;^McZQt2sxaQLRl zvi!VUF8??f41#8}8Oisi27>{b%_bbjK|Y_~E))t|0K#FVE)^CEg{^!(zkT)k3fvT$C zQ%XCnRtslmXOa3G9v;GV-EeMhZY!6|ZQo=>p!emg>pCpULOPxPr&ugL0}y-=^sao* z&d$(kwJecQ5d^WXGFWj@;7-QBirdwqI(8jjg+_p#X z9$n`99i{Y*VHj{62eK?bFBA&D-UYlDdYSKwVzKxO0E~~1HxxyAOeyWWZ|5Ca%l13A Sw5{y`0000X1^@s6-qmI800006VoOIv0RI60 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 O0000X1^@s6-qmI800006VoOIv0RI60 z0RN!9r;`8x1I|fAK~zY`m6gFu8)+EEpD}@HDwtUhqOe4AN`%5@4zZE`1&Pp$Y(1Dm zJQnsi2Txs3B7{W}@K7prA*m$nMR6m&=*=efFyk%p&={*s5HYcBVj9TIvxhM;QB%7Q zynOI}-}9S~m+yTCB+(tUT5T|$PQUQ`{T~6aU-TA&O#sc6Rokg@uJ>kH>S^In<_Wn&$KQ zd>R0HylGK(=-NyK?;IES(ep8q2M72)a&)fkx1k%0ER*#Rw|WVpr(;N zpY?r7BG=YP)9h?lDiwx8Ap&3|5_wy%*B>L5O3gYPj&B~1hpMXf2e#uLu&S!`csz7C z9N$u@)GQ_^C-(s`7K=R$`~f%?ixB{?d7#_vX0cf8tzkc4H~7t)PK}GjBHeB`0niQr zvMj^pa&=w=l1bp;02m(!o;|zC3j#1O02GVB-X8G!HSqj7Ffsy{%LQ4MaddP9I{=8H z2%F7zV~#|CL;{$a0^Ys5$zNOmrU|@%Z$anhfsqjafX!y>yypP`I6XZ@xm><6OA=tW zTQ@6}fc152ef0_mg@Cg&>&BAQv0PnU;r#sEf{LQ}J(tU2YikR4WVZt!J^*ub*81d$ zb$d~?D%+)_ zqtP(t=H}LI8yg!3x~~7aySw|_$;k;q)6;NdGQj@6Wzu!v>sO%B08|y&*|GM%d;uOk zLc{OJ!r~$}H#adgHMPIGy81^?n*yMsC^XK_$m3(OR3eX#$j_gx_0uOS+Bse4-)n0W zMUi7;V|q554Rm^+Br=&yNRp(A)9GX)ndHwXpU=04iV=-Q71K0@9;j)WLNppx02p4c z_ioGHQngxTI2=@rhlYl#i9{lNn{fwvr@o0q4^Y>21_A*BFcn3K8iq0OQ_z0( zH4KA_q7VQBfk0N*^|AZ>_d@Th@8#tslgT8VPA4Tvs$?>mP(N>9Xm@=DLEy~H3@0Wg z?mVCW4JC=~?d{<3@bE?Z*E~Hv{cojG`Q=BR2cf2E3X6-2e_jvsVlJ0^`jF>8TCl0p TFVs(X00000NkvXXu0mjfeAet4 literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/waypoint-add.png b/web/www/routino/icons/waypoint-add.png new file mode 100644 index 0000000000000000000000000000000000000000..c11fb26b519aa78d778f4b41e7836a312002139d GIT binary patch literal 163 zcmeAS@N?(olHy`uVBq!ia0vp^AT}2V8<6ZZI=>f4F%}28J29*~C-V}>VN3FMcVYMs zf(!O8p9~b?EbxddW?bP0l+XkK DMV2wm literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/waypoint-centre.png b/web/www/routino/icons/waypoint-centre.png new file mode 100644 index 0000000000000000000000000000000000000000..e4646311d01613a78984e1384fb45d59df9a9079 GIT binary patch literal 211 zcmeAS@N?(olHy`uVBq!ia0vp^+#t-s1|(OmDOUqhjKx9jP7LeL$-D$|*pj^6T^Rm@ z;DWu&Cj&(|3p^r=fm(z?n2}-D90{Nxdx@v7EBh^GRuKtdq1LksK%rny7sn8Z%UdTp z@-ZlKFkAomfBx>|E)O4@oP3iDVh6TqG2CAEX(9WBsf~x#tdE?%WfrNB73R67sN8AN y?h09ZWA&*QE!f@mWK8?0^<8$}x`X-tKN%iNSTbsfUfB#Zp25@A&t;ucLK6V=4?+|G literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/waypoint-down.png b/web/www/routino/icons/waypoint-down.png new file mode 100644 index 0000000000000000000000000000000000000000..1082bfd2253baded31e469c85b29957cbc6085e8 GIT binary patch literal 201 zcmeAS@N?(olHy`uVBq!ia0vp^AT}2V8<6ZZI=>f4F%}28J29*~C-V}>VN3FMcVYMs zf(!O8p9~b?EbxddW?k44ofy`glX(f`FeQ1ryDH0xY{Fz*FJC&P-SmMIrJoHtziwEO0r%Fk&FC%l!ljj;`l zS}bsAMeXUjX^LM`CuSSHa!va&QIY55&0_~w{=0c==LOFDE$a-=9_BK1U&gkkReDm0 kk?Z}9f8Kc3pMAi({I#BfHUHAXKqoMGy85}Sb4q9e0N3tWWB>pF literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/waypoint-remove.png b/web/www/routino/icons/waypoint-remove.png new file mode 100644 index 0000000000000000000000000000000000000000..2fcb77ffab15ed6a295d60ea4fcde040d8f24947 GIT binary patch literal 241 zcmeAS@N?(olHy`uVBq!ia0vp^AT}2V8<6ZZI=>f4F%}28J29*~C-V}>VN3FMcVYMs zf(!O8p9~b?EbxddW?~jTuBvx^~HlX?8tPV=^`neK|=rVHMBb z%basoS2I1kST{9id#qwu!ClqO3#+rIzl&zG+{;k?fX}9Zxj^=9viI_oKNZS3<{!OZ grQV+Z<(dtnm`3_O^XA9Lf$m}OboFyt=akR{0Pc)Y-T(jq literal 0 HcmV?d00001 diff --git a/web/www/routino/icons/waypoint-up.png b/web/www/routino/icons/waypoint-up.png new file mode 100644 index 0000000000000000000000000000000000000000..6b1b5c04ec2fc71255c815af15c953a48df0773c GIT binary patch literal 201 zcmeAS@N?(olHy`uVBq!ia0vp^AT}2V8<6ZZI=>f4F%}28J29*~C-V}>VN3FMcVYMs zf(!O8p9~b?EbxddW?N6eV9#3eSk!2Nz2@sfWm{|Weur-9RbljVy?tC~`J)SNXQRxrKNI(X;) q-syUtx|-K_-cYgi6ilf1pC+!7>SD#QxAhUw9tKZWKbLh*2~7ZkctZOC literal 0 HcmV?d00001 diff --git a/web/www/routino/index.html b/web/www/routino/index.html new file mode 100644 index 0000000..831ef02 --- /dev/null +++ b/web/www/routino/index.html @@ -0,0 +1,70 @@ + + + + + + +Routino : Route Planner for OpenStreetMap Data + + + + + + + + + +
    + +

    Routino : Route Planner for OpenStreetMap Data

    + +
    +
    + + + + + +
    + + + + + + + + + + + + diff --git a/web/www/routino/maplayout-ie6-bugfixes.css b/web/www/routino/maplayout-ie6-bugfixes.css new file mode 100644 index 0000000..0760507 --- /dev/null +++ b/web/www/routino/maplayout-ie6-bugfixes.css @@ -0,0 +1,83 @@ +/* +// Routino Internet Explorer 6 map layout web page style sheet. +// +// Part of the Routino routing software. +// +// This file Copyright 2010 Andrew M. Bishop +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . +*/ + + +/*----------------------------------*/ +/* Body HTML formatting */ +/*----------------------------------*/ + +/* + Internet Explorer 6 doesn't understand 'postion: fixed' styles. The best that + can be done is the following to make it the equivalent of absolute positioning. + This is "well-known" problem, you can find details on the internet. +*/ + +* HTML +{ + overflow-x: auto; +} + +* HTML BODY +{ + height: 100%; + width: 100%; + overflow: auto; +} + + +/*-------------*/ +/* Right panel */ +/*-------------*/ + + +/* + Internet Explorer 6 ignores the fact that the map and attribution divs are + within the right_panel and positions them all over everything (probably due + to the previous hacks). The fix for this is to make the left edges of these + divs line up with where the edge of the right_panel is. +*/ + +DIV.map +{ + left: 23.5em !important; +} + +DIV.attribution +{ + left: 23.5em !important; +} + + +/* + In addition to the poor positioning we need to set a height and width of the + map so we guess what fits in the user's window. +*/ + +DIV.map +{ + width: 65%; + height: 90%; +} + +DIV.attribution +{ + width: 65%; +} diff --git a/web/www/routino/maplayout-ie7-bugfixes.css b/web/www/routino/maplayout-ie7-bugfixes.css new file mode 100644 index 0000000..705056e --- /dev/null +++ b/web/www/routino/maplayout-ie7-bugfixes.css @@ -0,0 +1,64 @@ +/* +// Routino Internet Explorer 7 map layout web page style sheet. +// +// Part of the Routino routing software. +// +// This file Copyright 2010 Andrew M. Bishop +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . +*/ + + + +/*-------------*/ +/* Right panel */ +/*-------------*/ + +/* + What seems to happen is that the map div in the right panel picks up the size + of the right_panel div itself but won't fill the area unless a width and + height are given. Using 100% width and height is then the whole size of the + right_panel and the border makes it bigger still. + + This fix makes the right_panel smaller all round, the map div has its edges + moved out to allow the border to be visible all round and has 100% size. The + attribution needs to be given a position outside of the right_panel to make + sure that is isn't covered by the map. +*/ + +DIV.right_panel +{ + top: 3px !important; + bottom: 1.7em !important; + right: 3px !important; + left: 23.7em !important; +} + +DIV.map +{ + top: -3px !important; + bottom: -3px !important; + right: -3px !important; + left: -3px !important; + + width: 100% !important; + height: 100% !important; +} + +DIV.attribution +{ + bottom: -1.7em !important; + + width: 100% !important; +} diff --git a/web/www/routino/maplayout.css b/web/www/routino/maplayout.css new file mode 100644 index 0000000..bf83bb8 --- /dev/null +++ b/web/www/routino/maplayout.css @@ -0,0 +1,101 @@ +/* +// Routino map layout web page style sheet. +// +// Part of the Routino routing software. +// +// This file Copyright 2008-2010 Andrew M. Bishop +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . +*/ + + +/*----------------------------------*/ +/* Body HTML formatting */ +/*----------------------------------*/ + +BODY +{ + /* fonts and text styles */ + + font-family: sans-serif; + font-size: medium; + + /* colours */ + + background-color: white; + color: black; +} + + +/*------------*/ +/* Left panel */ +/*------------*/ + +DIV.left_panel +{ + width: 23em; + + position: absolute; + top: 0; + bottom: 0; + right: auto; + left: 0; + + padding: 3px; +} + + +/*-------------*/ +/* Right panel */ +/*-------------*/ + +DIV.right_panel +{ + position: fixed; + top: 0; + bottom: 0; + right: 0; + left: 23.5em; +} + +DIV.map +{ + position: absolute; + top: 0; + bottom: 1.5em; + right: 0; + left: 0; + + border: 3px solid; + + text-align: center; +} + +DIV.attribution +{ + position: absolute; + top: auto; + bottom: 0; + right: 0; + left: 0; + + margin: 0px; + border: 0px; + padding: 0px; + + text-align: center; + + white-space: nowrap; + overflow: hidden; +} diff --git a/web/www/routino/noscript.cgi b/web/www/routino/noscript.cgi new file mode 100755 index 0000000..a460c81 --- /dev/null +++ b/web/www/routino/noscript.cgi @@ -0,0 +1,196 @@ +#!/usr/bin/perl +# +# Routino non-Javascript router CGI +# +# Part of the Routino routing software. +# +# This file Copyright 2008-2010 Andrew M. Bishop +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# + +# Use the generic router script +require "router.pl"; + +# Use the perl CGI module +use CGI ':cgi'; + +# Create the query and get the parameters + +$query=new CGI; + +@rawparams=$query->param; + +# Legal CGI parameters with regexp validity check + +%legalparams=( + "lon[1-9]" => "[-0-9.]+", + "lat[1-9]" => "[-0-9.]+", + "transport" => "[a-z]+", + "highway-[a-z]+" => "[0-9.]+", + "speed-[a-z]+" => "[0-9.]+", + "property-[a-z]+" => "[0-9.]+", + "oneway" => "(1|0|true|false|on|off)", + "weight" => "[0-9.]+", + "height" => "[0-9.]+", + "width" => "[0-9.]+", + "length" => "[0-9.]+", + "length" => "[0-9.]+", + + "language" => "[-a-zA-Z]+" + "submit" => "(shortest|quickest|link)", + "format" => "(html|gpx-route|gpx-track|text|text-all|form)" + ); + +# Validate the CGI parameters, ignore invalid ones + +foreach $key (@rawparams) + { + foreach $test (keys (%legalparams)) + { + if($key =~ m%^$test$%) + { + $value=$query->param($key); + + if($value =~ m%^$legalparams{$test}$%) + { + $cgiparams{$key}=$value; + last; + } + } + } + } + +# Get the important parameters + +$submit=$cgiparams{submit}; +delete $cgiparams{submit}; + +$format=$cgiparams{format}; +delete $cgiparams{format}; + +$format="form" if(!$format || ($submit ne "shortest" && $submit ne "quickest")); + +# Generate a custom URL + +$customurl=""; + +if($submit) + { + $customurl="noscript.cgi?"; + + foreach $key (sort (keys %cgiparams)) + { + $customurl.="$key=$cgiparams{$key};"; + } + + $customurl.="submit=custom"; + } + +# Fill in the default parameters + +%fullparams=FillInDefaults(%cgiparams); + +# Open template file before running the router (including changing directory) + +open(TEMPLATE,") + { + if (m%%) + { + $key=$1; + + m%type="([a-z]+)"%; + $type=$1; + + m%value="([a-z]+)"%; + $value=$1; + + if ($type eq "radio") + { + $checked=""; + $checked="checked" if($fullparams{$key} eq $value); + + s%>% $checked>%; + } + elsif ($type eq "checkbox") + { + $checked=""; + $checked="checked" if($fullparams{$key}); + + s%>% $checked>%; + } + elsif ($type eq "text") + { + s%>% value="$fullparams{$key}">%; + } + + print; + } + elsif (m%%) + { + s%%$customurl%; + + print if($submit); + } + elsif (m%%) + { + $results_section=1; + } + elsif (m%%) + { + $results_section=0; + } + elsif ($results_section) + { + s%%$router_Type%; + s%%$router_type%; + s%%$router_uuid%; + s%%$router_time%; + s%%$router_result%; + s%%$router_message%; + + print if($router_uuid); + } + else + { + print; + } + } + +close(TEMPLATE); diff --git a/web/www/routino/noscript.html b/web/www/routino/noscript.html new file mode 100644 index 0000000..79344e2 --- /dev/null +++ b/web/www/routino/noscript.html @@ -0,0 +1,70 @@ + + + + + + +Routino : Route Planner for OpenStreetMap Data (non-JavaScript) + + + + + + + + + +
    + +

    Routino : Route Planner for OpenStreetMap Data (non-JavaScript)

    + +
    +
    + + + + + +
    + +

    Page Moved

    + +
    + + + + + + + + + + + + diff --git a/web/www/routino/noscript.template.html b/web/www/routino/noscript.template.html new file mode 100644 index 0000000..40eebb6 --- /dev/null +++ b/web/www/routino/noscript.template.html @@ -0,0 +1,420 @@ + + + + + + +Routino : Route Planner for OpenStreetMap Data (non-JavaScript) + + + + + + + + + +
    + +

    Routino : Route Planner for OpenStreetMap Data (non-JavaScript)

    + +
    +
    + + + + + +
    + +

    Non Javascript Route Finder

    + +
    + +

    Locations

    + + When a route is calculated it will visit (as close as possible + for the selected transport type) each of the locations that have + been specified in the order given. + +

    + + + + + + + + + + + +
    Waypoint 1: + E + N +
    Waypoint 2: + E + N +
    Waypoint 3: + E + N +
    Waypoint 4: + E + N +
    Waypoint 5: + E + N +
    Waypoint 6: + E + N +
    Waypoint 7: + E + N +
    Waypoint 8: + E + N +
    Waypoint 9: + E + N +
    + +

    + +

    Transport Type

    + + Selecting a transport type will restrict the chosen route to + those on which it is allowed. Selecting one of the links here + will set default values for the other parameters. + +

    + + + + + + + + + + + + +
    Foot + +
    Horse + +
    Wheelchair + +
    Bicycle + +
    Moped + +
    Motorbike + +
    Motorcar + +
    Goods + +
    HGV + +
    PSV + +
    + +

    + +

    Highway Preferences

    + + The highway preference is selected as a percentage and routes are chosen that + try to follow the preferred highways. + For example if a "Primary" road is given a "110%" preference and a "Secondary" + road is given a "100%" preference then it means that a route on a Primary road + can be up to 10% longer than on a secondary road and still be selected. + +

    Speed Limits

    + + The speed limits chosen here for the different types of highway apply if the + highway has no other speed limit marked or it is higher than the chosen one. + +

    + + + + + + + + + + + + + + + +
    + Preference + Speed Limit +
    Motorway: + + % + + km/hr +
    Trunk: + + % + + km/hr +
    Primary: + + % + + km/hr +
    Secondary: + + % + + km/hr +
    Tertiary: + + % + + km/hr +
    Unclassified: + + % + + km/hr +
    Residential: + + % + + km/hr +
    Service: + + % + + km/hr +
    Track: + + % + + km/hr +
    Cycleway: + + % + + km/hr +
    Path: + + % + + km/hr +
    Steps: + + % + + km/hr +
    + +

    + +

    Property Preferences

    + + The property preference is selected as a percentage and routes are chosen that + try to follow highways with the preferred property. + For example if a "Paved" highway is given a "75%" preference then it means that + an unpaved highway is automatically given a "25%" preference so that a route on + a paved highway can be 3 times the length of an unpaved one and still be + selected. + +

    + + + + + + + +
    + Preference
    +
    Paved: + + % +
    Multiple Lanes: + + % +
    Bridge: + + % +
    Tunnel: + + % +
    + +

    + +

    Other Restrictions

    + + These allow a route to be found that avoids marked limits on + weight, height, width or length. It is also possible to ignore + one-way restrictions (e.g. if walking). + +

    + + + + + + + +
    Obey oneway: + + +
    Weight: + + tonnes +
    Height: + + metres +
    Width: + + metres +
    Length: + + metres +
    + +

    + +

    Output Format

    + + This allows for selection of the language of the generated output. + +

    + + + + +
    English (en) + +
    German (de) + +
    + +

    + + This allows for selection of the format of the generated output. + +

    + + + + + + + + +
    This HTML format +
    HTML route instructions +
    GPX track file +
    GPX route file +
    Text file +
    Full text file +
    + +

    + +

    Calculate Route

    + + + + +

    + + + +


    + +

    + + + + + + + + + + + +
    Result +
    +
    +
    HTML file: + Open +
    GPX track file: + Open +
    GPX route file: + Open +
    Text file: + Open +
    Full text file: + Open +
    +
    + +

    + + + +


    + +

    + +

    Create Link

    + + + Bookmarkable link with customised parameters + +
    + +
    + + + + + + + + + + + + diff --git a/web/www/routino/page-elements.css b/web/www/routino/page-elements.css new file mode 100644 index 0000000..722997d --- /dev/null +++ b/web/www/routino/page-elements.css @@ -0,0 +1,143 @@ +/* +// Style sheet for page elements. +// +// Part of the Routino routing software. +// +// This file Copyright 2008-2010 Andrew M. Bishop +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . +*/ + + +/*-------------*/ +/* Tabbed DIVs */ +/*-------------*/ + + +DIV.tab_box +{ + padding: 3px; + margin-top: 3px; + + width: 100%; +} + +DIV.tab_box SPAN.tab_selected +{ + border-top: 2px solid; + border-left: 2px solid; + border-right: 2px solid; + border-bottom: 1px solid white; + + z-index: 5; + + padding: 3px; + + font-weight: bold; + font-variant: small-caps; + + background: #FFF; +} + +DIV.tab_box SPAN.tab_unselected +{ + border-top: 1px solid; + border-left: 1px solid; + border-right: 1px solid; + + padding: 3px; + + cursor: pointer; + + font-variant: small-caps; + + background: #CCC; +} + +DIV.tab_box SPAN.tab_unselected:hover +{ + background: #DDD; +} + +DIV.tab_content +{ + width: auto; + + padding: 3px; + border: thin solid; +} + + +/*----------------*/ +/* Show/Hide DIVs */ +/*----------------*/ + + +DIV.hideshow_box +{ + min-height: 1em; + + border-top: 2px solid; + border-color: #AAA; + + padding-bottom: 2px; + + overflow: hidden; +} + +DIV.hideshow_box:first-child +{ + border-top: none; +} + +DIV.hideshow_box SPAN.hideshow_show +{ + float: right; + display: block; + + font-style: italic; + font-variant: small-caps; + + padding-right: 2px; + padding-left: 1px; + padding-top: 1px; + padding-bottom: 1px; + + cursor: pointer; + + background: #CCC; +} + +DIV.hideshow_box SPAN.hideshow_show:hover +{ + background: #DDD; +} + +DIV.hideshow_box SPAN.hideshow_hide +{ + display: none; +} + +SPAN.hideshow_title +{ + display: block; + + font-weight: bold; + text-decoration: underline; + + padding-top: 1px; + padding-bottom: 1px; + + background: #EEE; +} diff --git a/web/www/routino/page-elements.js b/web/www/routino/page-elements.js new file mode 100644 index 0000000..f751897 --- /dev/null +++ b/web/www/routino/page-elements.js @@ -0,0 +1,91 @@ +// +// Javascript for page elements. +// +// Part of the Routino routing software. +// +// This file Copyright 2008,2009 Andrew M. Bishop +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . +// + + +// +// Display one of the tabs and associated DIV and hide the others +// + +function tab_select(name) +{ + var tab=document.getElementById("tab_" + name); + + if(tab.className=="tab_selected") + {return;} + + // Hide the deslected tabs and DIVs + + var parent=tab.parentNode; + var child=parent.firstChild; + + do + { + if(String(child.id).substr(0,4)=="tab_") + { + var div=document.getElementById(child.id + "_div"); + + child.className="tab_unselected"; + div.style.display="none"; + } + + child=child.nextSibling; + } + while(child!=null); + + // Display the newly selected tab and DIV + + var div=document.getElementById(tab.id + "_div"); + + tab.className="tab_selected"; + div.style.display=""; +} + + +// +// Show the associated DIV +// + +function hideshow_show(name) +{ + var span1=document.getElementById("hideshow_" + name + "_show"); + var span2=document.getElementById("hideshow_" + name + "_hide"); + var div=document.getElementById("hideshow_" + name + "_div"); + + div.style.display=""; + span1.className="hideshow_hide"; + span2.className="hideshow_show"; +} + + +// +// Hide the associated DIV +// + +function hideshow_hide(name) +{ + var span1=document.getElementById("hideshow_" + name + "_show"); + var span2=document.getElementById("hideshow_" + name + "_hide"); + var div=document.getElementById("hideshow_" + name + "_div"); + + div.style.display="none"; + span2.className="hideshow_hide"; + span1.className="hideshow_show"; +} diff --git a/web/www/routino/paths.pl b/web/www/routino/paths.pl new file mode 100644 index 0000000..a896ec3 --- /dev/null +++ b/web/www/routino/paths.pl @@ -0,0 +1,32 @@ +# +# Routino CGI paths Perl script +# +# Part of the Routino routing software. +# +# This file Copyright 2008-2010 Andrew M. Bishop +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# + +# Directory path parameters + +# EDIT THIS to set the root directory for the non-web data files. +$root_dir="../.."; + +# EDIT THIS if you want to change the location of the individual directories. +$bin_dir="$root_dir/bin"; +$data_dir="$root_dir/data"; +$results_dir="$root_dir/results"; + +1; diff --git a/web/www/routino/results.cgi b/web/www/routino/results.cgi new file mode 100755 index 0000000..5236363 --- /dev/null +++ b/web/www/routino/results.cgi @@ -0,0 +1,74 @@ +#!/usr/bin/perl +# +# Routino router results retrieval CGI +# +# Part of the Routino routing software. +# +# This file Copyright 2008-2010 Andrew M. Bishop +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# + +# Use the directory paths script +require "paths.pl"; + +# Use the generic router script +require "router.pl"; + +# Use the perl CGI module +use CGI ':cgi'; + +# Create the query and get the parameters + +$query=new CGI; + +@rawparams=$query->param; + +# Legal CGI parameters with regexp validity check + +%legalparams=( + "type" => "(shortest|quickest)", + "format" => "(html|gpx-route|gpx-track|text|text-all)", + + "uuid" => "[0-9a-f]{32}" + ); + +# Validate the CGI parameters, ignore invalid ones + +foreach $key (@rawparams) + { + foreach $test (keys (%legalparams)) + { + if($key =~ m%^$test$%) + { + $value=$query->param($key); + + if($value =~ m%^$legalparams{$test}$%) + { + $cgiparams{$key}=$value; + last; + } + } + } + } + +# Parse the parameters + +$uuid =$cgiparams{"uuid"}; +$type =$cgiparams{"type"}; +$format=$cgiparams{"format"}; + +# Return the file + +ReturnOutput($uuid,$type,$format); diff --git a/web/www/routino/router.cgi b/web/www/routino/router.cgi new file mode 100755 index 0000000..41a6212 --- /dev/null +++ b/web/www/routino/router.cgi @@ -0,0 +1,105 @@ +#!/usr/bin/perl +# +# Routino interactive router CGI +# +# Part of the Routino routing software. +# +# This file Copyright 2008-2010 Andrew M. Bishop +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# + +# Use the generic router script +require "router.pl"; + +# Use the perl CGI module +use CGI ':cgi'; + +# Create the query and get the parameters + +$query=new CGI; + +@rawparams=$query->param; + +# Legal CGI parameters with regexp validity check + +%legalparams=( + "lon[1-9]" => "[-0-9.]+", + "lat[1-9]" => "[-0-9.]+", + "transport" => "[a-z]+", + "highway-[a-z]+" => "[0-9.]+", + "speed-[a-z]+" => "[0-9.]+", + "property-[a-z]+" => "[0-9.]+", + "oneway" => "(1|0|true|false|on|off)", + "weight" => "[0-9.]+", + "height" => "[0-9.]+", + "width" => "[0-9.]+", + "length" => "[0-9.]+", + "length" => "[0-9.]+", + + "language" => "[-a-zA-Z]+", + "type" => "(shortest|quickest)", + "format" => "(html|gpx-route|gpx-track|text|text-all)" + ); + +# Validate the CGI parameters, ignore invalid ones + +foreach $key (@rawparams) + { + foreach $test (keys (%legalparams)) + { + if($key =~ m%^$test$%) + { + $value=$query->param($key); + + if($value =~ m%^$legalparams{$test}$%) + { + $cgiparams{$key}=$value; + last; + } + } + } + } + +# Get the important parameters + +$type=$cgiparams{type}; +delete $cgiparams{type}; + +$format=$cgiparams{format}; +delete $cgiparams{format}; + +# Fill in the default parameters + +%fullparams=FillInDefaults(%cgiparams); + +# Run the router + +($router_uuid,$router_time,$router_result,$router_message)=RunRouter($type,%fullparams); + +# Return the output + +if($format) + { + ReturnOutput($router_uuid,$type,$format); + } +else + { + print header('text/plain'); + + print "$router_uuid\n"; + print "$router_time\n"; + print "$router_result\n"; + print "$router_message\n"; + } diff --git a/web/www/routino/router.css b/web/www/routino/router.css new file mode 100644 index 0000000..9bb90c0 --- /dev/null +++ b/web/www/routino/router.css @@ -0,0 +1,171 @@ +/* +// Routino router web page style sheet. +// +// Part of the Routino routing software. +// +// This file Copyright 2008-2010 Andrew M. Bishop +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . +*/ + + +/*--------------------------------*/ +/* Left panel - override defaults */ +/*--------------------------------*/ + +DIV.hideshow_box +{ + overflow-x: auto; +} + + +/*------------------------------*/ +/* Left panel - generic options */ +/*------------------------------*/ + +DIV.scrollable +{ + overflow: auto; + + height: 20em; +} + + +/*-----------------------------------*/ +/* Left panel - specific tab options */ +/*-----------------------------------*/ + +DIV#tab_options_div IMG +{ + cursor: pointer; + vertical-align: bottom; +} + +DIV#tab_options_div IMG:hover +{ + background: #F0F000; +} + +DIV#tab_options_div TABLE +{ + padding: 0; + border: 0 hidden; + margin: 0; +} + +DIV#tab_options_div TABLE TD +{ + padding: 0; + border: 0; + margin: 0; +} + +DIV#tab_options_div INPUT +{ + padding: 0; + border: 1px solid; + margin: 0; + + text-align: right; +} + +DIV#tab_options_div INPUT:hover +{ + background: #F0F0C0; +} + +DIV#tab_options_div INPUT#shortest +{ + margin: 3px; + border: 3px solid; + + border-color: #00FF00; + + background: #C0F0C0; + + text-align: center; +} + +DIV#tab_options_div INPUT#quickest +{ + margin: 3px; + border: 3px solid; + + border-color: #0000FF; + + background: #C0C0F0; + + text-align: center; +} + +DIV#tab_results_div TABLE +{ + border-collapse: collapse; + border: hidden; +} + +DIV#tab_results_div TD.distance +{ + text-align: left; +} + +DIV#tab_results_div TD.highway +{ + text-align: left; + padding-left: 10px; +} + +DIV#tab_results_div DIV#shortest_links A:hover +{ + background: #C0F0C0; +} + +DIV#tab_results_div DIV#shortest_route TR:hover +{ + cursor: pointer; + + background: #C0F0C0; +} + +DIV#tab_results_div DIV#quickest_links A:hover +{ + background: #C0C0F0; +} + +DIV#tab_results_div DIV#quickest_route TR:hover +{ + cursor: pointer; + + background: #C0C0F0; +} + + +/*-------------------------------------------------*/ +/* Popup - using the styles defined in HTML output */ +/*-------------------------------------------------*/ + +DIV.popup table {table-layout: fixed; border: none; border-collapse: collapse;} +DIV.popup tr {border: 0px;} +DIV.popup tr.c {display: none;} /* coords */ +DIV.popup tr.n {} /* node */ +DIV.popup tr.s {} /* segment */ +DIV.popup tr.t {font-weight: bold;} /* total */ +DIV.popup td.l {font-weight: bold;} +DIV.popup td.r {} +DIV.popup span.w {font-weight: bold;} /* waypoint */ +DIV.popup span.h {text-decoration: underline;} /* highway */ +DIV.popup span.d {} /* segment distance */ +DIV.popup span.j {font-style: italic;} /* total journey distance */ +DIV.popup span.t {font-variant: small-caps;} /* turn */ +DIV.popup span.b {font-variant: small-caps;} /* bearing */ diff --git a/web/www/routino/router.html b/web/www/routino/router.html new file mode 120000 index 0000000..9f14bc5 --- /dev/null +++ b/web/www/routino/router.html @@ -0,0 +1 @@ +router.html.en \ No newline at end of file diff --git a/web/www/routino/router.html.en b/web/www/routino/router.html.en new file mode 100644 index 0000000..ec1020e --- /dev/null +++ b/web/www/routino/router.html.en @@ -0,0 +1,550 @@ + + + + + + +Routino : Route Planner for OpenStreetMap Data + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + Options + Results + Data +
    + +
    + +
    +
    + Routino OpenStreetMap Router + This web page allows routing within the data collected by OpenStreetMap. + Select start and end points (click on the marker icons below), select routing preferences then find a route. + +
    + +
    + Show + Hide + Language + + + + +
    + +
    + Show + Hide + Waypoints +
    + + + + + + + + + + + +
    + Waypoint 1  + + E  + + N  + + o + ^ + +
    + ~ + v + - +
    + Waypoint 2  + + E  + + N  + + o + ^ + +
    + ~ + v + - +
    + Waypoint 3  + + E  + + N  + + o + ^ + +
    + ~ + v + - +
    + Waypoint 4  + + E  + + N  + + o + ^ + +
    + ~ + v + - +
    + Waypoint 5  + + E  + + N  + + o + ^ + +
    + ~ + v + - +
    + Waypoint 6  + + E  + + N  + + o + ^ + +
    + ~ + v + - +
    + Waypoint 7  + + E  + + N  + + o + ^ + +
    + ~ + v + - +
    + Waypoint 8  + + E  + + N  + + o + ^ + +
    + ~ + v + - +
    + Waypoint 9  + + E  + + N  + + o + ^ + +
    + ~ + v + - + +
    +
    +
    +
    + +
    + Show + Hide + Transport Type +
    + +
    Foot +
    Horse +
    Wheelchair +
    Bicycle +
    Moped +
    Motorbike +
    Motorcar +
    Goods +
    HGV +
    PSV +
    +
    +
    + +
    + Show + Hide + Highway Preferences + +
    + +
    + Show + Hide + Speed Limits + +
    + +
    + Show + Hide + Property Preferences + +
    + +
    + Show + Hide + Other Restrictions + +
    + +
    + Find + + +
    + + + +
    + Show + Hide + Help +
    +
    +

    + Quick Start +
    + Click on marker icons (above) to place them on the map (right). Then + drag them to the correct position. Zooming the map before placing the + markers is probably easiest. Alternatively type the latitude and + longitude into the boxes above. +

    + Select the transport type, allowed highway types, speed limits, highway + properties and other restrictions from the options above. + Select "Shortest" or "Quickest" to calculate the route and display it + on the map. +

    + Waypoints +
    + Clicking on the marker icons will toggle the display of them on the map. + When a route is calculated it will visit (as close as possible + for the selected transport type) each of the waypoints that have + markers on the map in the order given. +

    + Transport Type +
    + Selecting a transport type will restrict the chosen route to + those on which it is allowed and set default values for the + other parameters. +

    + Highway Preferences +
    + The highway preference is selected as a percentage and routes are chosen that + try to follow the preferred highways. + For example if a "Primary" road is given a "110%" preference and a "Secondary" + road is given a "100%" preference then it means that a route on a Primary road + can be up to 10% longer than on a secondary road and still be selected. +

    + Speed Limits +
    + The speed limits chosen here for the different types of highway apply if the + highway has no other speed limit marked or it is higher than the chosen one. +

    + Property Preferences +
    + The property preference is selected as a percentage and routes are chosen that + try to follow highways with the preferred property. + For example if a "Paved" highway is given a "75%" preference then it means that + an unpaved highway is automatically given a "25%" preference so that a route on + a paved highway can be 3 times the length of an unpaved one and still be + selected. +

    + Other Restrictions +
    + These allow a route to be found that avoids marked limits on + weight, height, width or length. It is also possible to ignore + one-way restrictions (e.g. if walking). +

    +
    +
    +
    +
    + + + + + + + +
    + + + +
    +
    + +
    + +
    + + + diff --git a/web/www/routino/router.js b/web/www/routino/router.js new file mode 100644 index 0000000..efb421f --- /dev/null +++ b/web/www/routino/router.js @@ -0,0 +1,1503 @@ +// +// Routino router web page Javascript +// +// Part of the Routino routing software. +// +// This file Copyright 2008-2010 Andrew M. Bishop +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . +// + +//////////////////////////////////////////////////////////////////////////////// +/////////////////////////// Routino default profile //////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +var routino={ // contains all default Routino options (generated using "--help-profile-js"). + + // Default transport type + transport: 'motorcar', + + // Transport types + transports: {foot: 1, horse: 2, wheelchair: 3, bicycle: 4, moped: 5, motorbike: 6, motorcar: 7, goods: 8, hgv: 9, psv: 10}, + + // Highway types + highways: {motorway: 1, trunk: 2, primary: 3, secondary: 4, tertiary: 5, unclassified: 6, residential: 7, service: 8, track: 9, cycleway: 10, path: 11, steps: 12}, + + // Property types + properties: {paved: 1, multilane: 2, bridge: 3, tunnel: 4}, + + // Restriction types + restrictions: {oneway: 1, weight: 2, height: 3, width: 4, length: 5}, + + // Allowed highways + profile_highway: { + motorway: {foot: 0, horse: 0, wheelchair: 0, bicycle: 0, moped: 0, motorbike: 100, motorcar: 100, goods: 100, hgv: 100, psv: 100}, + trunk: {foot: 40, horse: 25, wheelchair: 40, bicycle: 30, moped: 90, motorbike: 100, motorcar: 100, goods: 100, hgv: 100, psv: 100}, + primary: {foot: 50, horse: 50, wheelchair: 50, bicycle: 70, moped: 100, motorbike: 90, motorcar: 90, goods: 90, hgv: 90, psv: 90}, + secondary: {foot: 60, horse: 50, wheelchair: 60, bicycle: 80, moped: 90, motorbike: 80, motorcar: 80, goods: 80, hgv: 80, psv: 80}, + tertiary: {foot: 70, horse: 75, wheelchair: 70, bicycle: 90, moped: 80, motorbike: 70, motorcar: 70, goods: 70, hgv: 70, psv: 70}, + unclassified: {foot: 80, horse: 75, wheelchair: 80, bicycle: 90, moped: 70, motorbike: 60, motorcar: 60, goods: 60, hgv: 60, psv: 60}, + residential: {foot: 90, horse: 75, wheelchair: 90, bicycle: 90, moped: 60, motorbike: 50, motorcar: 50, goods: 50, hgv: 50, psv: 50}, + service: {foot: 90, horse: 75, wheelchair: 90, bicycle: 90, moped: 80, motorbike: 80, motorcar: 80, goods: 80, hgv: 80, psv: 80}, + track: {foot: 95, horse: 100, wheelchair: 95, bicycle: 90, moped: 0, motorbike: 0, motorcar: 0, goods: 0, hgv: 0, psv: 0}, + cycleway: {foot: 95, horse: 90, wheelchair: 95, bicycle: 100, moped: 0, motorbike: 0, motorcar: 0, goods: 0, hgv: 0, psv: 0}, + path: {foot: 100, horse: 100, wheelchair: 100, bicycle: 90, moped: 0, motorbike: 0, motorcar: 0, goods: 0, hgv: 0, psv: 0}, + steps: {foot: 80, horse: 0, wheelchair: 0, bicycle: 0, moped: 0, motorbike: 0, motorcar: 0, goods: 0, hgv: 0, psv: 0} + }, + + // Speed limits + profile_speed: { + motorway: {foot: 0, horse: 0, wheelchair: 0, bicycle: 0, moped: 48, motorbike: 112, motorcar: 112, goods: 96, hgv: 89, psv: 89}, + trunk: {foot: 4, horse: 8, wheelchair: 4, bicycle: 20, moped: 48, motorbike: 96, motorcar: 96, goods: 96, hgv: 80, psv: 80}, + primary: {foot: 4, horse: 8, wheelchair: 4, bicycle: 20, moped: 48, motorbike: 96, motorcar: 96, goods: 96, hgv: 80, psv: 80}, + secondary: {foot: 4, horse: 8, wheelchair: 4, bicycle: 20, moped: 48, motorbike: 88, motorcar: 88, goods: 88, hgv: 80, psv: 80}, + tertiary: {foot: 4, horse: 8, wheelchair: 4, bicycle: 20, moped: 48, motorbike: 80, motorcar: 80, goods: 80, hgv: 80, psv: 80}, + unclassified: {foot: 4, horse: 8, wheelchair: 4, bicycle: 20, moped: 48, motorbike: 64, motorcar: 64, goods: 64, hgv: 64, psv: 64}, + residential: {foot: 4, horse: 8, wheelchair: 4, bicycle: 20, moped: 48, motorbike: 48, motorcar: 48, goods: 48, hgv: 48, psv: 48}, + service: {foot: 4, horse: 8, wheelchair: 4, bicycle: 20, moped: 32, motorbike: 32, motorcar: 32, goods: 32, hgv: 32, psv: 32}, + track: {foot: 4, horse: 8, wheelchair: 4, bicycle: 20, moped: 16, motorbike: 16, motorcar: 16, goods: 16, hgv: 16, psv: 16}, + cycleway: {foot: 4, horse: 8, wheelchair: 4, bicycle: 20, moped: 0, motorbike: 0, motorcar: 0, goods: 0, hgv: 0, psv: 0}, + path: {foot: 4, horse: 8, wheelchair: 4, bicycle: 20, moped: 0, motorbike: 0, motorcar: 0, goods: 0, hgv: 0, psv: 0}, + steps: {foot: 4, horse: 0, wheelchair: 4, bicycle: 0, moped: 0, motorbike: 0, motorcar: 0, goods: 0, hgv: 0, psv: 0} + }, + + // Highway properties + profile_property: { + paved: {foot: 50, horse: 20, wheelchair: 90, bicycle: 50, moped: 100, motorbike: 100, motorcar: 100, goods: 100, hgv: 100, psv: 100}, + multilane: {foot: 25, horse: 25, wheelchair: 25, bicycle: 25, moped: 25, motorbike: 75, motorcar: 75, goods: 75, hgv: 75, psv: 75}, + bridge: {foot: 50, horse: 50, wheelchair: 50, bicycle: 50, moped: 50, motorbike: 50, motorcar: 50, goods: 50, hgv: 50, psv: 50}, + tunnel: {foot: 50, horse: 50, wheelchair: 50, bicycle: 50, moped: 50, motorbike: 50, motorcar: 50, goods: 50, hgv: 50, psv: 50} + }, + + // Restrictions + profile_restrictions: { + oneway: {foot: 0, horse: 1, wheelchair: 0, bicycle: 1, moped: 1, motorbike: 1, motorcar: 1, goods: 1, hgv: 1, psv: 1}, + weight: {foot: 0.0, horse: 0.0, wheelchair: 0.0, bicycle: 0.0, moped: 0.0, motorbike: 0.0, motorcar: 0.0, goods: 5.0, hgv: 10.0, psv: 15.0}, + height: {foot: 0.0, horse: 0.0, wheelchair: 0.0, bicycle: 0.0, moped: 0.0, motorbike: 0.0, motorcar: 0.0, goods: 2.5, hgv: 3.0, psv: 3.0}, + width: {foot: 0.0, horse: 0.0, wheelchair: 0.0, bicycle: 0.0, moped: 0.0, motorbike: 0.0, motorcar: 0.0, goods: 2.0, hgv: 2.5, psv: 2.5}, + length: {foot: 0.0, horse: 0.0, wheelchair: 0.0, bicycle: 0.0, moped: 0.0, motorbike: 0.0, motorcar: 0.0, goods: 5.0, hgv: 6.0, psv: 6.0} + } + +}; // end of routino variable + +// Make a deep copy of the routino profile. + +var routino_default={}; +for(var l1 in routino) + if(typeof(routino[l1])!='object') + routino_default[l1]=routino[l1]; + else + { + routino_default[l1]={}; + for(var l2 in routino[l1]) + if(typeof(routino[l1][l2])!='object') + routino_default[l1][l2]=Number(routino[l1][l2]); + else + { + routino_default[l1][l2]={}; + for(var l3 in routino[l1][l2]) + routino_default[l1][l2][l3]=Number(routino[l1][l2][l3]); + } + } + + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////// Form handling ///////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +// +// Form initialisation - fill in the uninitialised parts +// + +function form_init() +{ + // Update the routino variable with the URL settings (from the HTML). + + for(var lang=0;lang< document.forms["form"].elements["language"].length;lang++) + if(document.forms["form"].elements["language"][lang].checked) + formSetLanguage(document.forms["form"].elements["language"][lang].value); + + var transport=null; + + for(var key in routino.transports) + if(document.forms["form"].elements["transport"][routino.transports[key]-1].checked) + transport=key; + + if(transport==null) + formSetTransport(routino.transport); + else + { + routino.transport=transport; + + for(var key in routino.profile_highway) + { + if(document.forms["form"].elements["highway-" + key].value=="") + document.forms["form"].elements["highway-" + key].value=routino.profile_highway[key][routino.transport]; + else + formSetHighway(key); + } + + for(var key in routino.profile_speed) + { + if(document.forms["form"].elements["speed-" + key].value=="") + document.forms["form"].elements["speed-" + key].value=routino.profile_speed[key][routino.transport]; + else + formSetSpeed(key); + } + + for(var key in routino.profile_property) + { + if(document.forms["form"].elements["property-" + key].value=="") + document.forms["form"].elements["property-" + key].value=routino.profile_property[key][routino.transport]; + else + formSetProperty(key); + } + + for(var key in routino.restrictions) + { + if(key=="oneway") + formSetRestriction(key); + else + { + if(document.forms["form"].elements["restrict-" + key].value=="") + document.forms["form"].elements["restrict-" + key].value=routino.profile_restrictions[key][routino.transport]; + else + formSetRestriction(key); + } + } + } + + // Delete the extra empty waypoints + + var filled=0; + + for(var marker=nmarkers;marker>=1;marker--) + { + var lon=document.forms["form"].elements["lon" + marker].value; + var lat=document.forms["form"].elements["lat" + marker].value; + + if(lon != "" && lat != "") + { + filled++; + markerAddMap(marker); + } + else if(filled==0) + markerRemove(marker); + } + + // Get the home location cookie and compare to each waypoint + + var cookies=document.cookie.split('; '); + + for(var cookie=0;cookie=1;marker--) + { + var lon=document.forms["form"].elements["lon" + marker].value; + var lat=document.forms["form"].elements["lat" + marker].value; + + if(lon==homelon && lat==homelat) + updateIcon(marker); + } + + // If the first location is empty and the cookie is set then fill it. + + if(document.forms["form"].elements["lon1"].value=="" && document.forms["form"].elements["lat1"].value=="") + { + document.forms["form"].elements["lon1"].value=homelon; + document.forms["form"].elements["lat1"].value=homelat; + + markerAddMap(1); + } + } + + updateCustomURL(); +} + + +// +// Change of language in the form +// + +function formSetLanguage(type) +{ + routino.language=type; + + updateCustomURL(); +} + + +// +// Change of transport in the form +// + +function formSetTransport(type) +{ + routino.transport=type; + + for(var key in routino.transports) + document.forms["form"].elements["transport"][routino.transports[key]-1].checked=(key==routino.transport); + + for(var key in routino.profile_highway) + document.forms["form"].elements["highway-" + key].value=routino.profile_highway[key][routino.transport]; + + for(var key in routino.profile_speed) + document.forms["form"].elements["speed-" + key].value=routino.profile_speed[key][routino.transport]; + + for(var key in routino.profile_property) + document.forms["form"].elements["property-" + key].value=routino.profile_property[key][routino.transport]; + + for(var key in routino.restrictions) + { + if(key=="oneway") + document.forms["form"].elements["restrict-" + key].checked=routino.profile_restrictions[key][routino.transport]; + else + document.forms["form"].elements["restrict-" + key].value=routino.profile_restrictions[key][routino.transport]; + } + + paramschanged=true; + + updateCustomURL(); +} + + +// +// Change of highway in the form +// + +function formSetHighway(type) +{ + routino.profile_highway[type][routino.transport]=document.forms["form"].elements["highway-" + type].value; + + paramschanged=true; + + updateCustomURL(); +} + + +// +// Change of Speed in the form +// + +function formSetSpeed(type) +{ + routino.profile_speed[type][routino.transport]=document.forms["form"].elements["speed-" + type].value; + + paramschanged=true; + + updateCustomURL(); +} + + +// +// Change of Property in the form +// + +function formSetProperty(type) +{ + routino.profile_property[type][routino.transport]=document.forms["form"].elements["property-" + type].value; + + paramschanged=true; + + updateCustomURL(); +} + + +// +// Change of oneway rule in the form +// + +function formSetRestriction(type) +{ + if(type=="oneway") + routino.profile_restrictions[type][routino.transport]=document.forms["form"].elements["restrict-" + type].checked; + else + routino.profile_restrictions[type][routino.transport]=document.forms["form"].elements["restrict-" + type].value; + + paramschanged=true; + + updateCustomURL(); +} + + +// +// Set the feature coordinates from the form when the form changes. +// + +function formSetCoords(marker) +{ + var lonlat=map.getCenter().clone(); + + lonlat.transform(map.getProjectionObject(),epsg4326); + + var lon=document.forms["form"].elements["lon" + marker].value; + var lat=document.forms["form"].elements["lat" + marker].value; + + if(lon!="") + { + if(lon<-180) lon=-180; + if(lon>+180) lon=+180; + lonlat.lon=lon; + } + + if(lat!="") + { + if(lat<-90 ) lat=-90 ; + if(lat>+90 ) lat=+90 ; + lonlat.lat=lat; + } + + var point = lonlat.clone(); + + point.transform(epsg4326,map.getProjectionObject()); + + markers[marker].move(point); + + markersmoved=true; + + coordsSetForm(marker); +} + + +// +// Set the feature coordinates in the form. +// + +function coordsSetForm(marker) +{ + var lonlat = new OpenLayers.LonLat(markers[marker].geometry.x, markers[marker].geometry.y); + lonlat.transform(map.getProjectionObject(),epsg4326); + + var lon=format5f(lonlat.lon); + var lat=format5f(lonlat.lat); + + document.forms["form"].elements["lon" + marker].value=lon; + document.forms["form"].elements["lat" + marker].value=lat; + + updateIcon(marker); + + updateCustomURL(); +} + + +// +// Format a number in printf("%.5f") format. +// + +function format5f(number) +{ + var newnumber=Math.floor(number*100000+0.5); + var delta=0; + + if(newnumber>=0 && newnumber<100000) delta= 100000; + if(newnumber<0 && newnumber>-100000) delta=-100000; + + var string=String(newnumber+delta); + + var intpart =string.substring(0,string.length-5); + var fracpart=string.substring(string.length-5,string.length); + + if(delta>0) intpart="0"; + if(delta<0) intpart="-0"; + + return(intpart + "." + fracpart); +} + + +// +// Build a set of URL arguments +// + +function buildURLArguments(all) +{ + var url="?"; + + url=url + "transport=" + routino.transport; + + for(var marker=1;marker<=vismarkers;marker++) + if(markers[marker].style.display == "" || all) + { + url=url + ";lon" + marker + "=" + document.forms["form"].elements["lon" + marker].value; + url=url + ";lat" + marker + "=" + document.forms["form"].elements["lat" + marker].value; + } + + for(var key in routino.profile_highway) + if(routino.profile_highway[key][routino.transport]!=routino_default.profile_highway[key][routino.transport]) + url=url + ";highway-" + key + "=" + routino.profile_highway[key][routino.transport]; + + for(var key in routino.profile_speed) + if(routino.profile_speed[key][routino.transport]!=routino_default.profile_speed[key][routino.transport]) + url=url + ";speed-" + key + "=" + routino.profile_speed[key][routino.transport]; + + for(var key in routino.profile_property) + if(routino.profile_property[key][routino.transport]!=routino_default.profile_property[key][routino.transport]) + url=url + ";property-" + key + "=" + routino.profile_property[key][routino.transport]; + + for(var key in routino.restrictions) + if(routino.profile_restrictions[key][routino.transport]!=routino_default.profile_restrictions[key][routino.transport]) + url=url + ";" + key + "=" + routino.profile_restrictions[key][routino.transport]; + + if(routino.language) + url=url + ";language=" + routino.language; + + return(url); +} + + +// +// Update custom URL +// + +function updateCustomURL() +{ + var visualiser_url=document.getElementById("visualiser_url"); + var link_url =document.getElementById("link_url"); + var edit_url =document.getElementById("edit_url"); + + visualiser_url.href="customvisualiser.cgi?" + map_args; + link_url.href="customrouter.cgi" + buildURLArguments(1) + ";" + map_args; + edit_url.href="http://www.openstreetmap.org/edit?" + map_args; +} + + +// +// Block the use of the return key to submit the form +// + +function block_return_key() +{ + var form=document.getElementById("form"); + + if(form.addEventListener) + form.addEventListener('keyup', discardReturnKey, false); + else if(form.attachEvent) + form.attachEvent('keyup', discardReturnKey); // Internet Explorer +} + +// +// Function to discard the return key if pressed +// + +function discardReturnKey(ev) +{ + if(ev.keyCode==13) + return(false); + + return(true); +} + + +//////////////////////////////////////////////////////////////////////////////// +///////////////////////////////// Map handling ///////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +var map; +var layerMapOSM, layerVectors, layerGPX; +var epsg4326, epsg900913; +var map_args; + +// +// Initialise the 'map' object +// + +function map_init(lat,lon,zoom) +{ + // Default configuration: + // UK coordinate range + // West -11.0, South 49.5, East 2.0, North 61.0 + // Zoom level 4 to 15 + + // EDIT THIS below to change the visible map limits + + var westedge = -11.0; // Minimum longitude (degrees) + var eastedge = 2.0; // Maximum longitude (degrees) + var southedge = 49.5; // Minimum latitude (degrees) + var northedge = 61.0; // Maximum latitude (degrees) + var zoomout = 4; // Minimum zoom + var zoomin = 15; // Maximum zoom + + // EDIT THIS above to change the visible map limits + + // + // Create the map + // + + epsg4326=new OpenLayers.Projection("EPSG:4326"); + epsg900913=new OpenLayers.Projection("EPSG:900913"); + + map = new OpenLayers.Map ("map", + { + controls:[ + new OpenLayers.Control.Navigation(), + new OpenLayers.Control.PanZoomBar(), + new OpenLayers.Control.ScaleLine(), + new OpenLayers.Control.LayerSwitcher() + ], + + projection: epsg900913, + displayProjection: epsg4326, + + minZoomLevel: zoomout, + numZoomLevels: zoomin-zoomout+1, + maxResolution: 156543.0339 / Math.pow(2,zoomout), + + maxExtent: new OpenLayers.Bounds(-20037508.34, -20037508.34, 20037508.34, 20037508.34), + restrictedExtent: new OpenLayers.Bounds(westedge,southedge,eastedge,northedge).transform(epsg4326,epsg900913), + + units: "m" + }); + + map.events.register("moveend", map, mapMoved); + + // Add a map tile layer (OpenStreetMap tiles, direct access) + + layerMapOSM = new OpenLayers.Layer.TMS("Original OSM map", + "http://tile.openstreetmap.org/", + { + emptyUrl: "http://openstreetmap.org/openlayers/img/404.png", + type: 'png', + getURL: limitedUrl, + displayOutsideMaxExtent: true, + buffer: 1 + }); + map.addLayer(layerMapOSM); + + // Get a URL for the tile; limited to map restricted extent. + + function limitedUrl(bounds) + { + var z = map.getZoom() + map.minZoomLevel; + + if (z>=7 && (bounds.right < map.restrictedExtent.left || + bounds.left > map.restrictedExtent.right || + bounds.top < map.restrictedExtent.bottom || + bounds.bottom > map.restrictedExtent.top)) + return this.emptyUrl; + + var res = map.getResolution(); + var y = Math.round((this.maxExtent.top - bounds.top) / (res * this.tileSize.h)); + var limit = Math.pow(2, z); + + if (y < 0 || y >= limit) + return this.emptyUrl; + + var x = Math.round((bounds.left - this.maxExtent.left) / (res * this.tileSize.w)); + + x = ((x % limit) + limit) % limit; + return this.url + z + "/" + x + "/" + y + "." + this.type; + } + + // Define a GPX layer but don't add it yet + + layerGPX={shortest: null, quickest: null}; + + gpx_style={shortest: new OpenLayers.Style({},{strokeWidth: 3, strokeColor: "#00FF00"}), + quickest: new OpenLayers.Style({},{strokeWidth: 3, strokeColor: "#0000FF"})}; + + // Add a vectors layer + + layerVectors = new OpenLayers.Layer.Vector("Markers"); + map.addLayer(layerVectors); + + // A set of markers + + nmarkers=99; + vismarkers=0; + markers={}; + markersmoved=false; + paramschanged=false; + + for(var marker=1;marker<=nmarkers;marker++) + { + if(document.forms["form"].elements["lon" + marker] != undefined) + { + markers[marker] = new OpenLayers.Feature.Vector(new OpenLayers.Geometry.Point(0,0),{}, + new OpenLayers.Style({},{externalGraphic: 'icons/marker-' + marker + '-red.png', + fillColor: "white", + graphicYOffset: -25, + graphicWidth: 21, + graphicHeight: 25, + display: "none"})); + + layerVectors.addFeatures([markers[marker]]); + } + else + { + nmarkers=marker-1; + vismarkers=marker-1; + break; + } + } + + // A function to drag the markers + + var drag = new OpenLayers.Control.DragFeature(layerVectors, + {onDrag: dragMove, + onComplete: dragComplete }); + map.addControl(drag); + drag.activate(); + + // Markers to highlight a selected point + + for(var highlight in highlights) + { + highlights[highlight] = new OpenLayers.Feature.Vector(new OpenLayers.Geometry.Point(0,0),{}, + new OpenLayers.Style({},{strokeColor: route_dark_colours[highlight], + fillColor: "white", + pointRadius: 10, + strokeWidth: 4, + fillOpacity: 0, + display: "none"})); + + layerVectors.addFeatures([highlights[highlight]]); + } + + // A popup for routing results + + for(var popup in popups) + popups[popup] = createPopup(popup); + + // Set the map centre to the limited range specified + + map.setCenter(map.restrictedExtent.getCenterLonLat(), map.getZoomForExtent(map.restrictedExtent,true)); + map.maxResolution = map.getResolution(); + + // Move the map + + if(lon != 'lon' && lat != 'lat' && zoom != 'zoom') + { + var lonlat = new OpenLayers.LonLat(lon,lat).transform(epsg4326,map.getProjectionObject()); + + map.moveTo(lonlat,zoom-map.minZoomLevel); + } +} + + +// +// Map has moved +// + +function mapMoved() +{ + var centre = map.getCenter().clone(); + + var lonlat = centre.transform(map.getProjectionObject(),epsg4326); + + var zoom = this.getZoom() + map.minZoomLevel; + + map_args="lat=" + lonlat.lat + ";lon=" + lonlat.lon + ";zoom=" + zoom; + + updateCustomURL(); +} + + +// +// OpenLayers.Control.DragFeature callback for a drag occuring. +// + +function dragMove(feature,pixel) +{ + for(var marker in markers) + if(feature==markers[marker]) + { + markersmoved=true; + + coordsSetForm(marker); + } +} + + +// +// OpenLayers.Control.DragFeature callback for completing a drag. +// + +function dragComplete(feature,pixel) +{ + for(var marker in markers) + if(feature==markers[marker]) + { + markersmoved=true; + + coordsSetForm(marker); + } +} + + +//////////////////////////////////////////////////////////////////////////////// +/////////////////////////////// Marker handling //////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +var nmarkers, vismarkers, markers, markersmoved, paramschanged; +var homelat=null, homelon=null; + + +// +// Toggle a marker on the map. +// + +function markerToggleMap(marker) +{ + if(markers[marker].style.display == "") + markerRemoveMap(marker); + else + markerAddMap(marker); +} + + +// +// Show a marker on the map. +// + +function markerAddMap(marker) +{ + markers[marker].style.display = ""; + + formSetCoords(marker); + + updateIcon(marker); + + markersmoved=true; +} + + +// +// Remove a marker from the map. +// + +function markerRemoveMap(marker) +{ + markers[marker].style.display = "none"; + + updateIcon(marker); + + markersmoved=true; +} + + +// +// Centre the marker on the map +// + +function markerCentre(marker) +{ + document.forms["form"].elements["lon" + marker].value=""; + document.forms["form"].elements["lat" + marker].value=""; + + formSetCoords(marker); + + markersmoved=true; +} + + +// +// Clear the current marker. +// + +function markerRemove(marker) +{ + for(var marker2=marker;marker2marker;marker2--) + { + document.forms["form"].elements["lon" + marker2].value=document.forms["form"].elements["lon" + (marker2-1)].value; + document.forms["form"].elements["lat" + marker2].value=document.forms["form"].elements["lat" + (marker2-1)].value; + + if(markers[marker2-1].style.display=="") + markerAddMap(marker2); + else + markerRemoveMap(marker2); + } + + document.forms["form"].elements["lon" + marker].value=""; + document.forms["form"].elements["lat" + marker].value=""; + markers[marker].style.display="none"; + + markerRemoveMap(marker); + + updateCustomURL(); +} + + +// +// Add a marker after the current one. +// + +function markerAddAfter(marker) +{ + if(vismarkers==nmarkers) + return false; + + vismarkers++; + + var marker_tr=document.getElementById("point" + vismarkers); + + marker_tr.style.display=""; + + for(var marker2=vismarkers;marker2>(marker+1);marker2--) + { + document.forms["form"].elements["lon" + marker2].value=document.forms["form"].elements["lon" + (marker2-1)].value; + document.forms["form"].elements["lat" + marker2].value=document.forms["form"].elements["lat" + (marker2-1)].value; + + if(markers[marker2-1].style.display=="") + markerAddMap(marker2); + else + markerRemoveMap(marker2); + } + + document.forms["form"].elements["lon" + (marker+1)].value=""; + document.forms["form"].elements["lat" + (marker+1)].value=""; + markers[marker+1].style.display="none"; + + markerRemoveMap(marker+1); + + updateCustomURL(); +} + + +// +// Set this marker as the home location. +// + +function markerHome(marker) +{ + if(markerHomeCookie(marker)) + for(marker=1;marker<=nmarkers;marker++) + updateIcon(marker); +} + + +// +// Update an icon to set colours and home or normal marker. +// + +function updateIcon(marker) +{ + var lon=document.forms["form"].elements["lon" + marker].value; + var lat=document.forms["form"].elements["lat" + marker].value; + + if(lon==homelon && lat==homelat) + { + if(markers[marker].style.display=="") + document.images["waypoint" + marker].src="icons/marker-home-red.png"; + else + document.images["waypoint" + marker].src="icons/marker-home-grey.png"; + + markers[marker].style.externalGraphic="icons/marker-home-red.png"; + } + else + { + if(markers[marker].style.display=="") + document.images["waypoint" + marker].src="icons/marker-" + marker + "-red.png"; + else + document.images["waypoint" + marker].src="icons/marker-" + marker + "-grey.png"; + + markers[marker].style.externalGraphic="icons/marker-" + marker + "-red.png"; + } + + layerVectors.drawFeature(markers[marker]); +} + + +// +// Set or clear the home marker icon +// + +function markerHomeCookie(marker) +{ + var lon=document.forms["form"].elements["lon" + marker].value; + var lat=document.forms["form"].elements["lat" + marker].value; + + if(lon=="" || lat=="") + return(false); + + var cookie; + var date = new Date(); + + if((homelat==null && homelon==null) || + (homelat!=lat && homelon!=lon)) + { + cookie="Routino-home=lon:" + lon + ":lat:" + lat; + + date.setUTCFullYear(date.getUTCFullYear()+5); + + homelat=lat; + homelon=lon; + } + else + { + cookie="Routino-home=unset"; + + date.setUTCFullYear(date.getUTCFullYear()-1); + + homelat=null; + homelon=null; + } + + document.cookie=cookie + ";expires=" + date.toGMTString(); + + return(true); +} + + +// +// Move this marker up. +// + +function markerMoveUp(marker) +{ + if(marker==1) + return false; + + markerSwap(marker,marker-1); +} + + +// +// Move this marker down. +// + +function markerMoveDown(marker) +{ + if(marker==vismarkers) + return false; + + markerSwap(marker,marker+1); +} + + +// +// Swap a pair of markers. +// + +function markerSwap(marker1,marker2) +{ + var lon=document.forms["form"].elements["lon" + marker1].value; + var lat=document.forms["form"].elements["lat" + marker1].value; + var display=markers[marker1].style.display; + + document.forms["form"].elements["lon" + marker1].value=document.forms["form"].elements["lon" + marker2].value; + document.forms["form"].elements["lat" + marker1].value=document.forms["form"].elements["lat" + marker2].value; + if(markers[marker2].style.display=="") + markerAddMap(marker1); + else + markerRemoveMap(marker1); + + document.forms["form"].elements["lon" + marker2].value=lon; + document.forms["form"].elements["lat" + marker2].value=lat; + if(display=="") + markerAddMap(marker2); + else + markerRemoveMap(marker2); + + updateCustomURL(); +} + + +// +// Reverse the markers. +// + +function markersReverse() +{ + for(var marker=1;marker<=vismarkers/2;marker++) + markerSwap(marker,vismarkers+1-marker); + + updateCustomURL(); +} + + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////// Route results handling //////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +var route_light_colours={shortest: "#60C060", quickest: "#6060C0"}; +var route_dark_colours ={shortest: "#408040", quickest: "#404080"}; + +var highlights={shortest: null, quickest: null}; +var popups={shortest: null, quickest: null}; +var routepoints={shortest: {}, quickest: {}}; +var gpx_style={shortest: null, quickest: null}; + +// +// Zoom to a specific item in the route +// + +function zoomTo(type,line) +{ + var lonlat = new OpenLayers.LonLat(routepoints[type][line].lon,routepoints[type][line].lat).transform(epsg4326,map.getProjectionObject()); + + map.moveTo(lonlat,map.numZoomLevels-2); +} + + +// +// Highlight a specific item in the route +// + +function highlight(type,line) +{ + if(line==-1) + { + highlights[type].style.display = "none"; + + drawPopup(popups[type],null); + } + else + { + // Marker + + var lonlat = new OpenLayers.LonLat(routepoints[type][line].lon,routepoints[type][line].lat).transform(epsg4326,map.getProjectionObject()); + + highlights[type].move(lonlat); + + if(highlights[type].style.display = "none") + highlights[type].style.display = ""; + + // Popup + + drawPopup(popups[type],"" + routepoints[type][line].html + "
    "); + } + + layerVectors.drawFeature(highlights[type]); +} + + +// +// Create a popup - not using OpenLayers because want it fixed on screen not fixed on map. +// + +function createPopup(type) +{ + var popup=document.createElement('div'); + + popup.className = "popup"; + + popup.innerHTML = ""; + + popup.style.display = "none"; + + popup.style.position = "fixed"; + popup.style.top = "-4000px"; + popup.style.left = "-4000px"; + popup.style.zIndex = "100"; + + popup.style.padding = "5px"; + + popup.style.opacity=0.85; + popup.style.backgroundColor=route_light_colours[type]; + popup.style.border="4px solid " + route_dark_colours[type]; + + document.body.appendChild(popup); + + return(popup); +} + + +// +// Draw a popup - not using OpenLayers because want it fixed on screen not fixed on map. +// + +function drawPopup(popup,html) +{ + if(html==null) + { + popup.style.display="none"; + return; + } + + if(popup.style.display=="none") + { + var map_div=document.getElementById("map"); + + popup.style.left =map_div.offsetParent.offsetLeft+map_div.offsetLeft+60 + "px"; + popup.style.top = map_div.offsetTop +30 + "px"; + popup.style.width =map_div.clientWidth-100 + "px"; + + popup.style.display=""; + } + + popup.innerHTML=html; +} + + +// +// Remove a GPX trace +// + +function removeGPXTrace(type) +{ + map.removeLayer(layerGPX[type]); + layerGPX[type].destroy(); + layerGPX[type]=null; + + displayStatus(type,"no_info"); + + var div_links=document.getElementById(type + "_links"); + div_links.style.display = "none"; + + var div_route=document.getElementById(type + "_route"); + div_route.innerHTML = ""; + + hideshow_hide(type); +} + + +//////////////////////////////////////////////////////////////////////////////// +/////////////////////////////// Server handling //////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +// +// Display data statistics +// + +function displayStatistics() +{ + // Use AJAX to get the statistics + + OpenLayers.loadURL("statistics.cgi",null,null,runStatisticsSuccess); +} + + +// +// Success in running data statistics generation. +// + +function runStatisticsSuccess(response) +{ + var statistics_data=document.getElementById("statistics_data"); + var statistics_link=document.getElementById("statistics_link"); + + statistics_data.innerHTML="
    " + response.responseText + "
    "; + + statistics_link.style.display="none"; +} + + +// +// Submit form - perform the routing +// + +function findRoute(type) +{ + tab_select("results"); + + hideshow_hide('help_options'); + hideshow_hide('shortest'); + hideshow_hide('quickest'); + + displayStatus("result","running"); + + var url="router.cgi" + buildURLArguments(0) + ";type=" + type; + + // Destroy the existing layer(s) + + if(markersmoved || paramschanged) + { + if(layerGPX.shortest!=null) + removeGPXTrace("shortest"); + if(layerGPX.quickest!=null) + removeGPXTrace("quickest"); + markersmoved=false; + paramschanged=false; + } + else if(layerGPX[type]!=null) + removeGPXTrace(type); + + // Use AJAX to run the router + + routing_type=type; + + OpenLayers.loadURL(url,null,null,runRouterSuccess,runRouterFailure); +} + + +// +// Success in running router. +// + +function runRouterSuccess(response) +{ + var lines=response.responseText.split('\n'); + + var uuid=lines[0]; + var cpuinfo=lines[1]; + var distinfo=lines[2]; + var message=lines[3]; + + // Update the status message + + if(message!="") + { + displayStatus("result","error"); + hideshow_show('help_route'); + return; + } + else + { + displayStatus("result","complete"); + hideshow_hide('help_route'); + } + + // Update the routing result message + + displayStatus(routing_type,"info",distinfo.bold()); + + var link; + + link=document.getElementById(routing_type + "_html"); + link.href="results.cgi?uuid=" + uuid + ";type=" + routing_type + ";format=html"; + link=document.getElementById(routing_type + "_gpx_track"); + link.href="results.cgi?uuid=" + uuid + ";type=" + routing_type + ";format=gpx-track"; + link=document.getElementById(routing_type + "_gpx_route"); + link.href="results.cgi?uuid=" + uuid + ";type=" + routing_type + ";format=gpx-route"; + link=document.getElementById(routing_type + "_text_all"); + link.href="results.cgi?uuid=" + uuid + ";type=" + routing_type + ";format=text-all"; + link=document.getElementById(routing_type + "_text"); + link.href="results.cgi?uuid=" + uuid + ";type=" + routing_type + ";format=text"; + + var div_links=document.getElementById(routing_type + "_links"); + div_links.style.display = ""; + + // Add a GPX layer + + var url="results.cgi?uuid=" + uuid + ";type=" + routing_type + ";format=gpx-track"; + + layerGPX[routing_type] = new OpenLayers.Layer.GML("GPX (" + routing_type + ")", url, + { + format: OpenLayers.Format.GPX, + style: gpx_style[routing_type], + projection: map.displayProjection + }); + + map.addLayer(layerGPX[routing_type]); + + hideshow_show(routing_type); + + displayResult(routing_type,uuid); +} + + +// +// Failure in running router. +// + +function runRouterFailure(response) +{ + displayStatus("result","failed"); +} + + +// +// Display the status +// + +function displayStatus(type,subtype,content) +{ + var div_status=document.getElementById(type + "_status"); + + var child=div_status.firstChild; + + do + { + if(child.id != undefined) + child.style.display="none"; + + child=child.nextSibling; + } + while(child != undefined); + + var span_status=document.getElementById(type + "_status_" + subtype); + + span_status.style.display=""; + + if(content != null) + span_status.innerHTML=content; +} + + +// +// Display the route +// + +function displayResult(type,uuid) +{ + routing_type = type; + + // Add the route + + var url="results.cgi?uuid=" + uuid + ";type=" + routing_type + ";format=html"; + + // Use AJAX to get the route + + OpenLayers.loadURL(url,null,null,getRouteSuccess,getRouteFailure); +} + + +// +// Success in getting route. +// + +function getRouteSuccess(response) +{ + var lines=response.responseText.split('\n'); + var div_route=document.getElementById(routing_type + "_route"); + + routepoints[routing_type]=[]; + + var points=routepoints[routing_type]; + + var table=0; + var point=0; + var total_table,total_word; + + for(var line=0;line')) + table=1; + else + continue; + } + + if(thisline.match('')) + break; + + if(thisline.match('')) + { + var rowtype=RegExp.$1; + + if(rowtype=='c') + { + thisline.match(' *([-0-9.]+) *([-0-9.]+)'); + points[point]={lat: Number(RegExp.$1), lon: Number(RegExp.$2), html: "", highway: "", distance: "", total: ""}; + + point++; + } + else if(rowtype=='n') + { + points[point-1].html += thisline; + } + else if(rowtype=='s') + { + thisline.match('([^<]+)'); + points[point-1].highway = RegExp.$1; + + thisline.match('([^<]+)'); + points[point-1].distance = RegExp.$1; + + thisline.match('([^<]+)'); + points[point-1].total = RegExp.$1; + + thisline.match('^(.*).'); + + points[point-1].html += RegExp.$1; + } + else if(rowtype=='t') + { + points[point-1].html += thisline; + + thisline.match('^(.*)'); + total_table = RegExp.$1; + + thisline.match('([^<]+)<'); + total_word = RegExp.$1; + + thisline.match('([^<]+)'); + points[point-1].total = RegExp.$1; + } + } + } + + var result=""; + + for(var p=0;p" + + "" + + "
    #" + (p+1) + + "" + points[p].highway; + } + + result=result + "
    " + total_word + " " + points[p].total; + + result=result + "
    "; + + div_route.innerHTML=result; +} + + +// +// Failure in getting route. +// + +function getRouteFailure(response) +{ + var div_route=document.getElementById(routing_type + "_route"); + div_route.innerHTML = ""; +} diff --git a/web/www/routino/router.pl b/web/www/routino/router.pl new file mode 100644 index 0000000..7f5dbd0 --- /dev/null +++ b/web/www/routino/router.pl @@ -0,0 +1,249 @@ +# +# Routino generic router Perl script +# +# Part of the Routino routing software. +# +# This file Copyright 2008-2010 Andrew M. Bishop +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# + +# Use the directory paths script +require "paths.pl"; + +# Use the perl Time::HiRes module +use Time::HiRes qw(gettimeofday tv_interval); + +$t0 = [gettimeofday]; + +# Filename prefix + +$data_prefix=""; + +$routino={ # contains all default Routino options (generated using "--help-profile-pl"). + + # Default transport type + transport => 'motorcar', + + # Transport types + transports => {foot => 1, horse => 2, wheelchair => 3, bicycle => 4, moped => 5, motorbike => 6, motorcar => 7, goods => 8, hgv => 9, psv => 10}, + + # Highway types + highways => {motorway => 1, trunk => 2, primary => 3, secondary => 4, tertiary => 5, unclassified => 6, residential => 7, service => 8, track => 9, cycleway => 10, path => 11, steps => 12}, + + # Property types + properties => {paved => 1, multilane => 2, bridge => 3, tunnel => 4}, + + # Restriction types + restrictions => {oneway => 1, weight => 2, height => 3, width => 4, length => 5}, + + # Allowed highways + profile_highway => { + motorway => { foot => 0, horse => 0, wheelchair => 0, bicycle => 0, moped => 0, motorbike => 100, motorcar => 100, goods => 100, hgv => 100, psv => 100}, + trunk => { foot => 40, horse => 25, wheelchair => 40, bicycle => 30, moped => 90, motorbike => 100, motorcar => 100, goods => 100, hgv => 100, psv => 100}, + primary => { foot => 50, horse => 50, wheelchair => 50, bicycle => 70, moped => 100, motorbike => 90, motorcar => 90, goods => 90, hgv => 90, psv => 90}, + secondary => { foot => 60, horse => 50, wheelchair => 60, bicycle => 80, moped => 90, motorbike => 80, motorcar => 80, goods => 80, hgv => 80, psv => 80}, + tertiary => { foot => 70, horse => 75, wheelchair => 70, bicycle => 90, moped => 80, motorbike => 70, motorcar => 70, goods => 70, hgv => 70, psv => 70}, + unclassified => { foot => 80, horse => 75, wheelchair => 80, bicycle => 90, moped => 70, motorbike => 60, motorcar => 60, goods => 60, hgv => 60, psv => 60}, + residential => { foot => 90, horse => 75, wheelchair => 90, bicycle => 90, moped => 60, motorbike => 50, motorcar => 50, goods => 50, hgv => 50, psv => 50}, + service => { foot => 90, horse => 75, wheelchair => 90, bicycle => 90, moped => 80, motorbike => 80, motorcar => 80, goods => 80, hgv => 80, psv => 80}, + track => { foot => 95, horse => 100, wheelchair => 95, bicycle => 90, moped => 0, motorbike => 0, motorcar => 0, goods => 0, hgv => 0, psv => 0}, + cycleway => { foot => 95, horse => 90, wheelchair => 95, bicycle => 100, moped => 0, motorbike => 0, motorcar => 0, goods => 0, hgv => 0, psv => 0}, + path => { foot => 100, horse => 100, wheelchair => 100, bicycle => 90, moped => 0, motorbike => 0, motorcar => 0, goods => 0, hgv => 0, psv => 0}, + steps => { foot => 80, horse => 0, wheelchair => 0, bicycle => 0, moped => 0, motorbike => 0, motorcar => 0, goods => 0, hgv => 0, psv => 0} + }, + + # Speed limits + profile_speed => { + motorway => { foot => 0, horse => 0, wheelchair => 0, bicycle => 0, moped => 48, motorbike => 112, motorcar => 112, goods => 96, hgv => 89, psv => 89}, + trunk => { foot => 4, horse => 8, wheelchair => 4, bicycle => 20, moped => 48, motorbike => 96, motorcar => 96, goods => 96, hgv => 80, psv => 80}, + primary => { foot => 4, horse => 8, wheelchair => 4, bicycle => 20, moped => 48, motorbike => 96, motorcar => 96, goods => 96, hgv => 80, psv => 80}, + secondary => { foot => 4, horse => 8, wheelchair => 4, bicycle => 20, moped => 48, motorbike => 88, motorcar => 88, goods => 88, hgv => 80, psv => 80}, + tertiary => { foot => 4, horse => 8, wheelchair => 4, bicycle => 20, moped => 48, motorbike => 80, motorcar => 80, goods => 80, hgv => 80, psv => 80}, + unclassified => { foot => 4, horse => 8, wheelchair => 4, bicycle => 20, moped => 48, motorbike => 64, motorcar => 64, goods => 64, hgv => 64, psv => 64}, + residential => { foot => 4, horse => 8, wheelchair => 4, bicycle => 20, moped => 48, motorbike => 48, motorcar => 48, goods => 48, hgv => 48, psv => 48}, + service => { foot => 4, horse => 8, wheelchair => 4, bicycle => 20, moped => 32, motorbike => 32, motorcar => 32, goods => 32, hgv => 32, psv => 32}, + track => { foot => 4, horse => 8, wheelchair => 4, bicycle => 20, moped => 16, motorbike => 16, motorcar => 16, goods => 16, hgv => 16, psv => 16}, + cycleway => { foot => 4, horse => 8, wheelchair => 4, bicycle => 20, moped => 0, motorbike => 0, motorcar => 0, goods => 0, hgv => 0, psv => 0}, + path => { foot => 4, horse => 8, wheelchair => 4, bicycle => 20, moped => 0, motorbike => 0, motorcar => 0, goods => 0, hgv => 0, psv => 0}, + steps => { foot => 4, horse => 0, wheelchair => 4, bicycle => 0, moped => 0, motorbike => 0, motorcar => 0, goods => 0, hgv => 0, psv => 0} + }, + + # Highway properties + profile_property => { + paved => { foot => 50, horse => 20, wheelchair => 90, bicycle => 50, moped => 100, motorbike => 100, motorcar => 100, goods => 100, hgv => 100, psv => 100}, + multilane => { foot => 25, horse => 25, wheelchair => 25, bicycle => 25, moped => 25, motorbike => 75, motorcar => 75, goods => 75, hgv => 75, psv => 75}, + bridge => { foot => 50, horse => 50, wheelchair => 50, bicycle => 50, moped => 50, motorbike => 50, motorcar => 50, goods => 50, hgv => 50, psv => 50}, + tunnel => { foot => 50, horse => 50, wheelchair => 50, bicycle => 50, moped => 50, motorbike => 50, motorcar => 50, goods => 50, hgv => 50, psv => 50} + }, + + # Restrictions + profile_restrictions => { + oneway => { foot => 0, horse => 1, wheelchair => 0, bicycle => 1, moped => 1, motorbike => 1, motorcar => 1, goods => 1, hgv => 1, psv => 1}, + weight => { foot => 0.0, horse => 0.0, wheelchair => 0.0, bicycle => 0.0, moped => 0.0, motorbike => 0.0, motorcar => 0.0, goods => 5.0, hgv => 10.0, psv => 15.0}, + height => { foot => 0.0, horse => 0.0, wheelchair => 0.0, bicycle => 0.0, moped => 0.0, motorbike => 0.0, motorcar => 0.0, goods => 2.5, hgv => 3.0, psv => 3.0}, + width => { foot => 0.0, horse => 0.0, wheelchair => 0.0, bicycle => 0.0, moped => 0.0, motorbike => 0.0, motorcar => 0.0, goods => 2.0, hgv => 2.5, psv => 2.5}, + length => { foot => 0.0, horse => 0.0, wheelchair => 0.0, bicycle => 0.0, moped => 0.0, motorbike => 0.0, motorcar => 0.0, goods => 5.0, hgv => 6.0, psv => 6.0} + }, + +}; # end of routino variable + + +# +# Fill in the default parameters using the ones above (don't use executable compiled in defaults) +# + +sub FillInDefaults + { + my(%params)=@_; + + $params{transport}=$routino->{transport} if(!defined $params{transport}); + + my($transport)=$params{transport}; + + foreach $highway (keys %{$routino->{highways}}) + { + $key="highway-$highway"; + $value=$routino->{profile_highway}->{$highway}->{$transport}; + $params{$key}=$value if(!defined $params{$key}); + + $key="speed-$highway"; + $value=$routino->{profile_speed}->{$highway}->{$transport}; + $params{$key}=$value if(!defined $params{$key}); + } + + foreach $property (keys %{$routino->{properties}}) + { + $key="property-$property"; + $value=$routino->{profile_property}->{$property}->{$transport}; + $params{$key}=$value if(!defined $params{$key}); + } + + $params{oneway} =~ s/(true|on)/1/; + $params{oneway} =~ s/(false|off)/0/; + + foreach $restriction (keys %{$routino->{restrictions}}) + { + $key="$restriction"; + $value=$routino->{profile_restrictions}->{$restriction}->{$transport}; + $params{$key}=$value if(!defined $params{$key}); + } + + return %params; + } + + +# +# Run the router +# + +sub RunRouter + { + my($optimise,%params)=@_; + + # Combine all of the parameters together + + my($params)="--$optimise"; + + foreach $key (keys %params) + { + $params.=" --$key=$params{$key}"; + } + + # Change directory + + mkdir $results_dir,0755 if(! -d $results_dir); + chdir $results_dir; + + # Create a unique output directory + + chomp($uuid=`echo '$params' $$ | md5sum | cut -f1 '-d '`); + + mkdir $uuid; + chmod 0775, $uuid; + chdir $uuid; + + # Run the router + + $params.=" --dir=$data_dir" if($data_dir); + $params.=" --prefix=$data_prefix" if($data_prefix); + $params.=" --quiet"; + + $message=`$bin_dir/router $params 2>&1`; + + (undef,undef,$cuser,$csystem) = times; + $time=sprintf "time: %.3f CPU / %.3f elapsed",$cuser+$csystem,tv_interval($t0); + + if(-f "$optimise.txt") + { + $result=`tail -1 $optimise.txt`; + @result=split(/\t/,$result); + $result = $result[4]." , ".$result[5]; + } + + # Return the results + + return($uuid,$time,$result,$message); + } + + +# +# Return the output file +# + +# Possible file formats + +%suffixes=( + "html" => ".html", + "gpx-route" => "-route.gpx", + "gpx-track" => "-track.gpx", + "text" => ".txt", + "text-all" => "-all.txt" + ); + +# Possible MIME types + +%mimetypes=( + "html" => "text/html", + "gpx-route" => "text/xml", + "gpx-track" => "text/xml", + "text" => "text/plain", + "text-all" => "text/plain" + ); + +sub ReturnOutput + { + my($uuid,$type,$format)=@_; + + $suffix=$suffixes{$format}; + $mime =$mimetypes{$format}; + + $file="$results_dir/$uuid/$type$suffix"; + + # Return the output + + if(!$type || !$uuid || !$format || ! -f $file) + { + print header('text/plain','404 Not found'); + print "Not Found!\n"; + } + else + { + print header($mime); + + system "cat $file"; + } + } + +1; diff --git a/web/www/routino/statistics.cgi b/web/www/routino/statistics.cgi new file mode 100755 index 0000000..501097a --- /dev/null +++ b/web/www/routino/statistics.cgi @@ -0,0 +1,39 @@ +#!/usr/bin/perl +# +# Routino data statistics +# +# Part of the Routino routing software. +# +# This file Copyright 2008,2009 Andrew M. Bishop +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# + +# Use the directory paths script +require "paths.pl"; + +# Use the perl CGI module +use CGI ':cgi'; + +# Print the output + +print header('text/plain'); + +# Run the filedumper + +$params.=" --dir=$data_dir" if($data_dir); +$params.=" --prefix=$data_prefix" if($data_prefix); +$params.=" --statistics"; + +system "$bin_dir/filedumper $params 2>&1"; diff --git a/web/www/routino/visualiser.cgi b/web/www/routino/visualiser.cgi new file mode 100755 index 0000000..d3b8f38 --- /dev/null +++ b/web/www/routino/visualiser.cgi @@ -0,0 +1,110 @@ +#!/usr/bin/perl +# +# Routino data visualiser CGI +# +# Part of the Routino routing software. +# +# This file Copyright 2008,2009 Andrew M. Bishop +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# + +# Use the directory paths script +require "paths.pl"; + +# Use the perl CGI module +use CGI ':cgi'; + +# Create the query and get the parameters + +$query=new CGI; + +@rawparams=$query->param; + +# Legal CGI parameters with regexp validity check + +%legalparams=( + "latmin" => "[-0-9.]+", + "latmax" => "[-0-9.]+", + "lonmin" => "[-0-9.]+", + "lonmax" => "[-0-9.]+", + "data" => "(junctions|super|oneway|speed|weight|height|width|length)" + ); + +# Validate the CGI parameters, ignore invalid ones + +foreach $key (@rawparams) + { + foreach $test (keys (%legalparams)) + { + if($key =~ m%^$test$%) + { + $value=$query->param($key); + + if($value =~ m%^$legalparams{$test}$%) + { + $cgiparams{$key}=$value; + last; + } + } + } + } + +# Parameters to limit range selected + +%limits=( + "junctions" => 0.1, + "speed" => 0.2, + "super" => 0.2, + "oneway" => 0.2, + "weight" => 0.3, + "height" => 0.3, + "width" => 0.3, + "length" => 0.3 + ); + +# Check the parameters + +$latmin=$cgiparams{"latmin"}; +$latmax=$cgiparams{"latmax"}; +$lonmin=$cgiparams{"lonmin"}; +$lonmax=$cgiparams{"lonmax"}; +$data =$cgiparams{"data"}; + +if($latmin eq "" || $latmax eq "" || $lonmin eq "" || $lonmax eq "" || $data eq "") + { + print header(-status => '500 Invalid CGI parameters'); + exit; + } + +if(($latmax-$latmin)>$limits{$data} || ($lonmax-$lonmin)>$limits{$data}) + { + print header(-status => '500 Selected area too large'); + exit; + } + +# Print the output + +print header('text/plain'); + +print "$latmin $lonmin $latmax $lonmax\n"; + +# Run the filedumper + +$params.=" --dir=$data_dir" if($data_dir); +$params.=" --prefix=$data_prefix" if($data_prefix); +$params.=" --visualiser --data=$data"; +$params.=" --latmin=$latmin --latmax=$latmax --lonmin=$lonmin --lonmax=$lonmax"; + +system "$bin_dir/filedumper $params 2>&1"; diff --git a/web/www/routino/visualiser.css b/web/www/routino/visualiser.css new file mode 100644 index 0000000..561544e --- /dev/null +++ b/web/www/routino/visualiser.css @@ -0,0 +1,49 @@ +/* +// Routino visualiser web page style sheet. +// +// Part of the Routino routing software. +// +// This file Copyright 2008-2010 Andrew M. Bishop +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . +*/ + + +/*--------------------------------*/ +/* Left panel - override defaults */ +/*--------------------------------*/ + +DIV.hideshow_box +{ + overflow-x: auto; +} + + +/*-----------------------------------*/ +/* Left panel - specific tab options */ +/*-----------------------------------*/ + +DIV#tab_visualiser_div INPUT +{ + padding: 0; + border: 1px solid; + margin: 0; + + text-align: center; +} + +DIV#tab_visualiser_div INPUT:hover +{ + background: #F0F0C0; +} diff --git a/web/www/routino/visualiser.html b/web/www/routino/visualiser.html new file mode 100644 index 0000000..c79bea9 --- /dev/null +++ b/web/www/routino/visualiser.html @@ -0,0 +1,258 @@ + + + + + + +Routino : Data Visualiser for Routino OpenStreetMap Data + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + Visualiser + Router + Data +
    + +
    + +
    + Routino Data Visualiser + This web page allows visualisation of the data that Routino uses for routing. + Only data relevant for routing is displayed and some will therefore be excluded + (e.g. private roads). + +
    + +
    + Instructions + Zoom in and then use the buttons below to download the data. The + server will only return data if the selected area is small enough. +
    + +
    + Status +
    + No data displayed +
    +
    + +
    + Show + Hide + + +
    + +
    + Show + Hide + + +
    + +
    + Show + Hide + + +
    + +
    + Show + Hide + + +
    + +
    + Show + Hide + + +
    + +
    + Show + Hide + + +
    + +
    + Show + Hide + + +
    + +
    + Show + Hide + + +
    + +
    + +
    + + +
    + + + + + +
    + + + +
    +
    + +
    + +
    + + + diff --git a/web/www/routino/visualiser.js b/web/www/routino/visualiser.js new file mode 100644 index 0000000..1444c89 --- /dev/null +++ b/web/www/routino/visualiser.js @@ -0,0 +1,618 @@ +// +// Routino data visualiser web page Javascript +// +// Part of the Routino routing software. +// +// This file Copyright 2008-2010 Andrew M. Bishop +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . +// + + +// +// Data types +// + +var data_types=[ + "junctions", + "super", + "oneway", + "speed", + "weight", + "height", + "width", + "length" + ]; + + +// +// Junction styles +// + +var junction_colours={ + 0: "#FFFFFF", + 1: "#FF0000", + 2: "#FFFF00", + 3: "#00FF00", + 4: "#8B4513", + 5: "#00BFFF", + 6: "#FF69B4", + 7: "#000000", + 8: "#000000", + 9: "#000000" + }; + +var junction_styles={}; + + +// +// Super styles +// + +var super_node_style,super_segment_style; + + +// +// Oneway styles +// + +var hex={0: "00", 1: "11", 2: "22", 3: "33", 4: "44", 5: "55", 6: "66", 7: "77", + 8: "88", 9: "99", 10: "AA", 11: "BB", 12: "CC", 13: "DD", 14: "EE", 15: "FF"}; + + +//////////////////////////////////////////////////////////////////////////////// +///////////////////////////////// Map handling ///////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +var map; +var layerMapOSM, layerVectors, layerBoxes; +var epsg4326, epsg900913; +var map_args; + +var box; + +// +// Initialise the 'map' object +// + +function map_init(lat,lon,zoom) +{ + // Default configuration: + // UK coordinate range + // West -11.0, South 49.5, East 2.0, North 61.0 + // Zoom level 4 to 15 + + // EDIT THIS below to change the visible map limits + + var westedge = -11.0; // Minimum longitude (degrees) + var eastedge = 2.0; // Maximum longitude (degrees) + var southedge = 49.5; // Minimum latitude (degrees) + var northedge = 61.0; // Maximum latitude (degrees) + var zoomout = 4; // Minimum zoom + var zoomin = 15; // Maximum zoom + + // EDIT THIS above to change the visible map limits + + // + // Create the map + // + + epsg4326=new OpenLayers.Projection("EPSG:4326"); + epsg900913=new OpenLayers.Projection("EPSG:900913"); + + map = new OpenLayers.Map ("map", + { + controls:[ + new OpenLayers.Control.Navigation(), + new OpenLayers.Control.PanZoomBar(), + new OpenLayers.Control.ScaleLine(), + new OpenLayers.Control.LayerSwitcher() + ], + + projection: epsg900913, + displayProjection: epsg4326, + + minZoomLevel: zoomout, + numZoomLevels: zoomin-zoomout+1, + maxResolution: 156543.0339 / Math.pow(2,zoomout), + + maxExtent: new OpenLayers.Bounds(-20037508.34, -20037508.34, 20037508.34, 20037508.34), + restrictedExtent: new OpenLayers.Bounds(westedge,southedge,eastedge,northedge).transform(epsg4326,epsg900913), + + units: "m" + }); + + map.events.register("moveend", map, mapMoved); + + // Add a map tile layer (OpenStreetMap tiles, direct access) + + layerMapOSM = new OpenLayers.Layer.TMS("Original OSM map", + "http://tile.openstreetmap.org/", + { + emptyUrl: "http://openstreetmap.org/openlayers/img/404.png", + type: 'png', + getURL: limitedUrl, + displayOutsideMaxExtent: true, + buffer: 1 + }); + map.addLayer(layerMapOSM); + + // Get a URL for the tile; limited to map restricted extent. + + function limitedUrl(bounds) + { + var z = map.getZoom() + map.minZoomLevel; + + if (z>=7 && (bounds.right < map.restrictedExtent.left || + bounds.left > map.restrictedExtent.right || + bounds.top < map.restrictedExtent.bottom || + bounds.bottom > map.restrictedExtent.top)) + return this.emptyUrl; + + var res = map.getResolution(); + var y = Math.round((this.maxExtent.top - bounds.top) / (res * this.tileSize.h)); + var limit = Math.pow(2, z); + + if (y < 0 || y >= limit) + return this.emptyUrl; + + var x = Math.round((bounds.left - this.maxExtent.left) / (res * this.tileSize.w)); + + x = ((x % limit) + limit) % limit; + return this.url + z + "/" + x + "/" + y + "." + this.type; + } + + // Add a vectors layer + + layerVectors = new OpenLayers.Layer.Vector("Markers"); + map.addLayer(layerVectors); + + for(var colour in junction_colours) + junction_styles[colour]=new OpenLayers.Style({},{stroke: false, pointRadius: 2,fillColor: junction_colours[colour]}); + + super_node_style =new OpenLayers.Style({},{stroke: false, pointRadius: 3,fillColor : "#FF0000"}); + super_segment_style=new OpenLayers.Style({},{fill: false , strokeWidth: 2,strokeColor: "#FF0000"}); + + // Add a boxes layer + + layerBoxes = new OpenLayers.Layer.Boxes("Boundary"); + map.addLayer(layerBoxes); + + box=null; + + // Set the map centre to the limited range specified + + map.setCenter(map.restrictedExtent.getCenterLonLat(), map.getZoomForExtent(map.restrictedExtent,true)); + map.maxResolution = map.getResolution(); + + // Move the map + + if(lon != 'lon' && lat != 'lat' && zoom != 'zoom') + { + var lonlat = new OpenLayers.LonLat(lon,lat).transform(epsg4326,map.getProjectionObject()); + + map.moveTo(lonlat,zoom-map.minZoomLevel); + } +} + + +// +// Map has moved +// + +function mapMoved() +{ + var centre = map.getCenter().clone(); + + var lonlat = centre.transform(map.getProjectionObject(),epsg4326); + + var zoom = this.getZoom() + map.minZoomLevel; + + map_args="lat=" + lonlat.lat + ";lon=" + lonlat.lon + ";zoom=" + zoom; + + updateCustomURL(); +} + + +// +// Update custom URL +// + +function updateCustomURL() +{ + var router_url=document.getElementById("router_url"); + var link_url =document.getElementById("link_url"); + var edit_url =document.getElementById("edit_url"); + + router_url.href="customrouter.cgi?" + map_args; + link_url.href="customvisualiser.cgi?" + map_args; + edit_url.href="http://www.openstreetmap.org/edit?" + map_args; +} + + +//////////////////////////////////////////////////////////////////////////////// +/////////////////////////////// Server handling //////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +// +// Display data statistics +// + +function displayStatistics() +{ + // Use AJAX to get the statistics + + OpenLayers.loadURL("statistics.cgi",null,null,runStatisticsSuccess); +} + + +// +// Success in running data statistics generation. +// + +function runStatisticsSuccess(response) +{ + var statistics_data=document.getElementById("statistics_data"); + var statistics_link=document.getElementById("statistics_link"); + + statistics_data.innerHTML="
    " + response.responseText + "
    "; + + statistics_link.style.display="none"; +} + + +// +// Get the requested data +// + +function displayData(datatype) +{ + for(var data in data_types) + hideshow_hide(data_types[data]); + + if(datatype != "") + hideshow_show(datatype); + + // Delete the old data + + layerVectors.destroyFeatures(); + + if(box != null) + layerBoxes.removeMarker(box); + box=null; + + // Print the status + + var div_status=document.getElementById("result_status"); + div_status.innerHTML = "No data displayed"; + + // Return if just here to clear the data + + if(datatype == "") + return; + + // Get the new data + + var mapbounds=map.getExtent().clone(); + mapbounds.transform(epsg900913,epsg4326); + + var url="visualiser.cgi"; + + url=url + "?lonmin=" + mapbounds.left; + url=url + ";latmin=" + mapbounds.bottom; + url=url + ";lonmax=" + mapbounds.right; + url=url + ";latmax=" + mapbounds.top; + url=url + ";data=" + datatype; + + // Print the status + + div_status.innerHTML = "Fetching " + datatype + " data ..."; + + // Use AJAX to get the data + + switch(datatype) + { + case 'junctions': + OpenLayers.loadURL(url,null,null,runJunctionsSuccess,runFailure); + break; + case 'super': + OpenLayers.loadURL(url,null,null,runSuperSuccess,runFailure); + break; + case 'oneway': + OpenLayers.loadURL(url,null,null,runOnewaySuccess,runFailure); + break; + case 'speed': + case 'weight': + case 'height': + case 'width': + case 'length': + OpenLayers.loadURL(url,null,null,runLimitSuccess,runFailure); + break; + } +} + + +// +// Success in getting the junctions. +// + +function runJunctionsSuccess(response) +{ + var lines=response.responseText.split('\n'); + +// This won't update the browser window +// var div_status=document.getElementById("result_status"); +// div_status.innerHTML = "Processing " + (lines.length-2) + " junctions ..."; + + var features=[]; + + for(var line=0;line. +# + +WEBDIR=../web/data + +FILES=profiles.xml \ + translations.xml \ + tagging.xml + +######## + +all : + -@[ -d $(WEBDIR) ] && \ + for file in $(FILES); do \ + if [ ! -f $(WEBDIR)/$$file ] || [ routino-$$file -nt $(WEBDIR)/$$file ]; then \ + echo cp routino-$$file $(WEBDIR)/$$file ;\ + cp -f routino-$$file $(WEBDIR)/$$file ;\ + fi ;\ + done + +######## + +clean: + rm -f *~ + +######## + +distclean: clean + rm -f $(WEBDIR)/*.xml diff --git a/xml/osm.xsd b/xml/osm.xsd new file mode 100644 index 0000000..1b657a9 --- /dev/null +++ b/xml/osm.xsd @@ -0,0 +1,112 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/xml/routino-osm.xsd b/xml/routino-osm.xsd new file mode 100644 index 0000000..092990a --- /dev/null +++ b/xml/routino-osm.xsd @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/xml/routino-profiles.xml b/xml/routino-profiles.xml new file mode 100644 index 0000000..ba3fa51 --- /dev/null +++ b/xml/routino-profiles.xml @@ -0,0 +1,461 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/xml/routino-profiles.xsd b/xml/routino-profiles.xsd new file mode 100644 index 0000000..7c13e94 --- /dev/null +++ b/xml/routino-profiles.xsd @@ -0,0 +1,108 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/xml/routino-tagging-nomodify.xml b/xml/routino-tagging-nomodify.xml new file mode 100644 index 0000000..0674870 --- /dev/null +++ b/xml/routino-tagging-nomodify.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/xml/routino-tagging.xml b/xml/routino-tagging.xml new file mode 100644 index 0000000..0bec4dc --- /dev/null +++ b/xml/routino-tagging.xml @@ -0,0 +1,435 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/xml/routino-tagging.xsd b/xml/routino-tagging.xsd new file mode 100644 index 0000000..7df6dae --- /dev/null +++ b/xml/routino-tagging.xsd @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/xml/routino-translations.xml b/xml/routino-translations.xml new file mode 100644 index 0000000..470563d --- /dev/null +++ b/xml/routino-translations.xml @@ -0,0 +1,177 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <!-- %s = [shortest|quickest] --> + + <start string="Start" text="At %s, head %s" /> <!-- 1st %s = [waypoint|junction], 2nd %s = [heading] --> + <node string="At" text="%s, go %s heading %s" /> <!-- 1st %s = [waypoint|junction], 2nd %s = [turn], 3rd %s = [heading] --> + <segment string="Follow" text="%s for %.3f km, %.1f min" /> <!-- 1st %s = street name --> + <stop string="Stop" text="At %s" /> <!-- 1st %s = [waypoint|junction] --> + <total string="Total" text="%.1f km, %.0f minutes" /> + </output-html> + + <!-- GPX output --> + <output-gpx> + <waypoint type="start" string="START" /> <!-- For the first route waypoint --> + <waypoint type="inter" string="INTER" /> <!-- For the intermediate route waypoints --> + <waypoint type="trip" string="TRIP" /> <!-- For the other route points --> + <waypoint type="finish" string="FINISH"/> <!-- For the last route waypoint --> + + <desc text="%s route between 'start' and 'finish' waypoints" /> <!-- %s = [shortest|quickest] --> + <name text="%s route" /> <!-- %s = [shortest|quickest] --> + <step text="%s on '%s' for %.3f km, %.1f min" /> <!-- 1st %s = [turn], 2nd %s = street name --> + <final text="Total Journey %.1f km, %.0f minutes" /> + </output-gpx> + + </language> + + <language lang="de"> + + <!-- Copyright of the data being routed, not of this file --> + <copyright> + <creator string="Creator" text="Routino - http://www.routino.org/" /> + <source string="Source" text="Basierend auf OpenStreetMap-Daten, erhältlich via http://www.openstreetmap.org/" /> + <license string="License" text="http://creativecommons.org/licenses/by-sa/2.0/" /> + </copyright> + + <!-- Turn directions, 0 = ahead, -2 = left, +/-4 = behind, +2 = right --> + <turn direction="-4" string="Spitzkehre nach links" /> + <turn direction="-3" string="Scharf links" /> + <turn direction="-2" string="Links" /> + <turn direction="-1" string="Halb links" /> + <turn direction="0" string="Geradeaus" /> + <turn direction="1" string="Halb rechts" /> + <turn direction="2" string="Rechts" /> + <turn direction="3" string="Scharf rechts" /> + <turn direction="4" string="Spitzkehre nach rechts" /> + + <!-- Heading directions, 0 = North, -2 = West, +/-4 = South, +2 = East --> + <heading direction="-4" string="Süd" /> + <heading direction="-3" string="Süd-West" /> + <heading direction="-2" string="West" /> + <heading direction="-1" string="Nord-West" /> + <heading direction="0" string="Nord" /> + <heading direction="1" string="Nord-Ost" /> + <heading direction="2" string="Ost" /> + <heading direction="3" string="Süd-Ost" /> + <heading direction="4" string="Süd" /> + + <!-- Highway names --> + <highway type="motorway" string="Autobahn" /> + <highway type="trunk" string="Schnellstraße" /> + <highway type="primary" string="Bundesstraße" /> + <highway type="secondary" string="Landstraße" /> + <highway type="tertiary" string="Kreisstraße" /> + <highway type="unclassified" string="Nebenstraße" /> + <highway type="residential" string="Wohngebietsstraße" /> + <highway type="service" string="Erschließungsweg" /> + <highway type="track" string="Wirtschaftsweg" /> + <highway type="cycleway" string="Radweg" /> + <highway type="path" string="Weg" /> + <highway type="steps" string="Treppe" /> + + <!-- The type of route --> + <route type="shortest" string="Kürzeste" /> <!-- For the description and route name --> + <route type="quickest" string="Schnellste" /> <!-- For the description and route name --> + + <!-- HTML output --> + <output-html> + <waypoint type="waypoint" string="Wegpunkt" /> <!-- For the chosen waypoints --> + <waypoint type="junction" string="Anschlussstelle" /> <!-- For the interesting junctions --> + + <title text="%s Route" /> <!-- %s = [shortest|quickest] --> + + <start string="Start" text="Bei %s halten Sie sich Richtung %s" /> <!-- 1st %s = [waypoint|junction], 2nd %s = [heading] --> + <node string="Bei" text="Bei %s wenden Sie sich nach %s Richtung %s" /> <!-- 1st %s = [waypoint|junction], 2nd %s = [turn], 3rd %s = [heading] --> + <segment string="Folgen" text="Folgen Sie der %s für %.3f km bzw. %.1f min" /> <!-- 1st %s = street name --> + <stop string="Stop" text="Sie sind bei %s angekommen" /> <!-- 1st %s = [waypoint|junction] --> + <total string="Gesamt" text="%.1f km, %.0f minuten" /> + </output-html> + + <!-- GPX output --> + <output-gpx> + <waypoint type="start" string="START" /> <!-- For the first route waypoint --> + <waypoint type="inter" string="INTER" /> <!-- For the intermediate route waypoints --> + <waypoint type="trip" string="TRIP" /> <!-- For the other route points --> + <waypoint type="finish" string="FINISH"/> <!-- For the last route waypoint --> + + <desc text="%s Strecke zwischen 'Start' und 'Ziel'" /> <!-- %s = [shortest|quickest] --> + <name text="%s Strecke" /> <!-- %s = [shortest|quickest] --> + <step text="%s auf '%s' für %.3f km, %.1f min" /> <!-- 1st %s = [turn], 2nd %s = street name --> + <final text="Gesamtstrecke %.1f km, %.0f minuten" /> + </output-gpx> + + </language> + +</routino-translations> diff --git a/xml/routino-translations.xsd b/xml/routino-translations.xsd new file mode 100644 index 0000000..75fbfab --- /dev/null +++ b/xml/routino-translations.xsd @@ -0,0 +1,171 @@ +<?xml version="1.0" encoding="utf-8"?> + +<!-- ============================================================ + $Header: /home/amb/routino/xml/RCS/routino-translations.xsd,v 1.3 2010/05/29 13:54:43 amb Exp $ + + An XML Schema Definition for the Routino translations XML format + + Part of the Routino routing software. + ============================================================ + This file Copyright 2010 Andrew M. Bishop + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + ============================================================ --> + +<xsd:schema elementFormDefault="qualified" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> + + <!-- The top level Routino translation --> + + <xsd:element name="routino-translations" type="RoutinoTranslationsType"/> + + <xsd:complexType name="RoutinoTranslationsType"> + <xsd:sequence> + <xsd:element name="language" type="languageType" minOccurs="0" maxOccurs="unbounded"/> + </xsd:sequence> + </xsd:complexType> + + <xsd:complexType name="languageType"> + <xsd:sequence> + <xsd:element name="copyright" type="CopyrightType" minOccurs="0"/> + <xsd:element name="turn" type="TurnType" minOccurs="0" maxOccurs="unbounded"/> + <xsd:element name="heading" type="HeadingType" minOccurs="0" maxOccurs="unbounded"/> + <xsd:element name="highway" type="HighwayType" minOccurs="0" maxOccurs="unbounded"/> + <xsd:element name="route" type="RouteType" minOccurs="0" maxOccurs="2"/> + <xsd:element name="output-html" type="HTMLType" minOccurs="0"/> + <xsd:element name="output-gpx" type="GPXType" minOccurs="0"/> + </xsd:sequence> + <xsd:attribute name="lang" type="xsd:string"/> + </xsd:complexType> + + <!-- The copyright information (of the generated output, not of this file) --> + + <xsd:complexType name="CopyrightType"> + <xsd:sequence> + <xsd:element name="creator" type="CopyrightCreatorType" minOccurs="0"/> + <xsd:element name="source" type="CopyrightSourceType" minOccurs="0"/> + <xsd:element name="license" type="CopyrightLicenseType" minOccurs="0"/> + </xsd:sequence> + </xsd:complexType> + + <xsd:complexType name="CopyrightCreatorType"> + <xsd:attribute name="string" type="xsd:string"/> + <xsd:attribute name="text" type="xsd:string"/> + </xsd:complexType> + + <xsd:complexType name="CopyrightSourceType"> + <xsd:attribute name="string" type="xsd:string"/> + <xsd:attribute name="text" type="xsd:string"/> + </xsd:complexType> + + <xsd:complexType name="CopyrightLicenseType"> + <xsd:attribute name="string" type="xsd:string"/> + <xsd:attribute name="text" type="xsd:string"/> + </xsd:complexType> + + <!-- The turn, heading, highway and route strings --> + + <xsd:complexType name="TurnType"> + <xsd:attribute name="direction" type="xsd:string"/> + <xsd:attribute name="string" type="xsd:string"/> + </xsd:complexType> + + <xsd:complexType name="HeadingType"> + <xsd:attribute name="direction" type="xsd:string"/> + <xsd:attribute name="string" type="xsd:string"/> + </xsd:complexType> + + <xsd:complexType name="HighwayType"> + <xsd:attribute name="type" type="xsd:string"/> + <xsd:attribute name="string" type="xsd:string"/> + </xsd:complexType> + + <xsd:complexType name="RouteType"> + <xsd:attribute name="type" type="xsd:string"/> + <xsd:attribute name="string" type="xsd:string"/> + </xsd:complexType> + + <!-- The HTML output strings --> + + <xsd:complexType name="HTMLType"> + <xsd:sequence> + <xsd:element name="waypoint" type="HTMLWaypointType" maxOccurs="2"/> + <xsd:element name="title" type="HTMLTitleType"/> + <xsd:element name="start" type="HTMLStartType"/> + <xsd:element name="node" type="HTMLNodeType"/> + <xsd:element name="segment" type="HTMLSegmentType"/> + <xsd:element name="stop" type="HTMLStopType"/> + <xsd:element name="total" type="HTMLTotalType"/> + </xsd:sequence> + </xsd:complexType> + + <xsd:complexType name="HTMLWaypointType"> + <xsd:attribute name="type" type="xsd:string"/> + <xsd:attribute name="string" type="xsd:string"/> + </xsd:complexType> + + <xsd:complexType name="HTMLTitleType"> + <xsd:attribute name="text" type="xsd:string"/> + </xsd:complexType> + + <xsd:complexType name="HTMLStartType"> + <xsd:attribute name="string" type="xsd:string"/> + <xsd:attribute name="text" type="xsd:string"/> + </xsd:complexType> + + <xsd:complexType name="HTMLNodeType"> + <xsd:attribute name="string" type="xsd:string"/> + <xsd:attribute name="text" type="xsd:string"/> + </xsd:complexType> + + <xsd:complexType name="HTMLSegmentType"> + <xsd:attribute name="string" type="xsd:string"/> + <xsd:attribute name="text" type="xsd:string"/> + </xsd:complexType> + + <xsd:complexType name="HTMLStopType"> + <xsd:attribute name="string" type="xsd:string"/> + <xsd:attribute name="text" type="xsd:string"/> + </xsd:complexType> + + <xsd:complexType name="HTMLTotalType"> + <xsd:attribute name="string" type="xsd:string"/> + <xsd:attribute name="text" type="xsd:string"/> + </xsd:complexType> + + <!-- The GPX output strings --> + + <xsd:complexType name="GPXType"> + <xsd:sequence> + <xsd:element name="waypoint" type="GPXWaypointType" maxOccurs="4"/> + <xsd:element name="desc" type="GPXDescType"/> + <xsd:element name="name" type="GPXNameType"/> + <xsd:element name="step" type="GPXStepType"/> + <xsd:element name="final" type="GPXFinalType"/> + </xsd:sequence> + </xsd:complexType> + + <xsd:complexType name="GPXWaypointType"> + <xsd:attribute name="type" type="xsd:string"/> + <xsd:attribute name="string" type="xsd:string"/> + </xsd:complexType> + + <xsd:complexType name="GPXDescType"> + <xsd:attribute name="text" type="xsd:string"/> + </xsd:complexType> + + <xsd:complexType name="GPXNameType"> + <xsd:attribute name="text" type="xsd:string"/> + </xsd:complexType> + + <xsd:complexType name="GPXStepType"> + <xsd:attribute name="text" type="xsd:string"/> + </xsd:complexType> + + <xsd:complexType name="GPXFinalType"> + <xsd:attribute name="text" type="xsd:string"/> + </xsd:complexType> + +</xsd:schema> diff --git a/xml/xsd.xsd b/xml/xsd.xsd new file mode 100644 index 0000000..3208cd3 --- /dev/null +++ b/xml/xsd.xsd @@ -0,0 +1,65 @@ +<?xml version="1.0" encoding="utf-8" ?> + +<!-- ============================================================ + $Header: /home/amb/routino/xml/RCS/xsd.xsd,v 1.1 2010/03/28 15:27:05 amb Exp $ + + An XML Schema Definition for the XML Schema Definition XML format + + Not a full definition but sufficient to allow the xsd-to-xmlparser to + read it to bootstrap itself - a program to read in other files in the + same format to create more XML parsers for other useful things. + ============================================================ + This file Copyright 2010 Andrew M. Bishop + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + ============================================================ --> + +<xsd:schema elementFormDefault="qualified" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> + + <!-- The top level xsd:schema element --> + + <xsd:element name="xsd:schema" type="schemaType"/> + + <xsd:complexType name="schemaType"> + <xsd:sequence> + <xsd:element name="xsd:element" type="elementType"/> + <xsd:element name="xsd:complexType" type="complexType" minOccurs="0" maxOccurs="unbounded"/> + </xsd:sequence> + <xsd:attribute name="elementFormDefault" type="xsd:string"/> + <xsd:attribute name="xmlns:xsd" type="xsd:string"/> + </xsd:complexType> + + <!-- The second level xsd:element and xsd:complexType elements --> + + <xsd:complexType name="elementType"> + <xsd:attribute name="name" type="xsd:string"/> + <xsd:attribute name="type" type="xsd:string"/> + <xsd:attribute name="minOccurs" type="xsd:string"/> + <xsd:attribute name="maxOccurs" type="xsd:string"/> + </xsd:complexType> + + <xsd:complexType name="complexType"> + <xsd:sequence> + <xsd:element name="xsd:sequence" type="sequenceType" minOccurs="0"/> + <xsd:element name="xsd:attribute" type="attributeType" minOccurs="0" maxOccurs="unbounded"/> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string"/> + </xsd:complexType> + + <!-- The third level elements and their contents --> + + <xsd:complexType name="sequenceType"> + <xsd:sequence> + <xsd:element name="xsd:element" type="elementType" minOccurs="0" maxOccurs="unbounded"/> + </xsd:sequence> + </xsd:complexType> + + <xsd:complexType name="attributeType"> + <xsd:attribute name="name" type="xsd:string"/> + <xsd:attribute name="type" type="xsd:string"/> + </xsd:complexType> + +</xsd:schema> -- 1.7.9.5